Index: trunk/src/org/openstreetmap/josm/data/cache/JCSCacheManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/cache/JCSCacheManager.java	(revision 17363)
+++ trunk/src/org/openstreetmap/josm/data/cache/JCSCacheManager.java	(revision 17364)
@@ -31,4 +31,5 @@
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.ImageResource;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
@@ -45,4 +46,10 @@
     private static final String PREFERENCE_PREFIX = "jcs.cache";
     public static final BooleanProperty USE_BLOCK_CACHE = new BooleanProperty(PREFERENCE_PREFIX + ".use_block_cache", true);
+
+    /**
+     * The preference key {@code jcs.cache.use_image_resource_cache} controls the caching mechanism used for {@link ImageResource}.
+     * If set to {@code true}, a combined memory/disk is used. Otherwise, an in-memory-cache is used.
+     */
+    public static final BooleanProperty USE_IMAGE_RESOURCE_CACHE = new BooleanProperty(PREFERENCE_PREFIX + ".use_image_resource_cache", false);
 
     private static final AuxiliaryCacheFactory DISK_CACHE_FACTORY =
@@ -171,11 +178,17 @@
      * @return cache access object
      */
+    public static <K, V> CacheAccess<K, V> getCache(String cacheName, int maxMemoryObjects, int maxDiskObjects, String cachePath) {
+        return getCache(cacheName, maxMemoryObjects, maxDiskObjects, cachePath, USE_BLOCK_CACHE.get() ? 4096 : 0);
+    }
+
     @SuppressWarnings("unchecked")
-    public static <K, V> CacheAccess<K, V> getCache(String cacheName, int maxMemoryObjects, int maxDiskObjects, String cachePath) {
+    private static <K, V> CacheAccess<K, V> getCache(String cacheName, int maxMemoryObjects, int maxDiskObjects,
+                                                     String cachePath, int blockSizeBytes) {
         CacheAccess<K, V> cacheAccess = JCS.getInstance(cacheName, getCacheAttributes(maxMemoryObjects));
         CompositeCache<K, V> cc = cacheAccess.getCacheControl();
 
         if (cachePath != null && cacheDirLock != null) {
-            IDiskCacheAttributes diskAttributes = getDiskCacheAttributes(maxDiskObjects, cachePath, cacheName);
+            IDiskCacheAttributes diskAttributes = getDiskCacheAttributes(maxDiskObjects, cachePath, cacheName, blockSizeBytes);
+            Logging.debug("Setting up cache: {0}", diskAttributes);
             try {
                 if (cc.getAuxCaches().length == 0) {
@@ -193,4 +206,19 @@
 
     /**
+     * Returns a cache for {@link ImageResource}
+     * @param <K> key type
+     * @param <V> value type
+     * @return cache access object
+     */
+    public static <K, V> CacheAccess<K, V> getImageResourceCache() {
+        if (!USE_IMAGE_RESOURCE_CACHE.get()) {
+            return getCache("images", 16 * 1024, 0, null);
+        }
+        String cachePath = new File(Config.getDirs().getCacheDirectory(true), "images").getAbsolutePath();
+        Logging.warn("Using experimental disk cache {0} for ImageResource", cachePath);
+        return getCache("images", 16 * 1024, 512 * 1024, cachePath, 1024);
+    }
+
+    /**
      * Close all files to ensure, that all indexes and data are properly written
      */
@@ -199,10 +227,11 @@
     }
 
-    private static IDiskCacheAttributes getDiskCacheAttributes(int maxDiskObjects, String cachePath, String cacheName) {
+    private static IDiskCacheAttributes getDiskCacheAttributes(int maxDiskObjects, String cachePath, String cacheName, int blockSizeBytes) {
         IDiskCacheAttributes ret;
-        removeStaleFiles(cachePath + File.separator + cacheName, USE_BLOCK_CACHE.get() ? "_INDEX_v2" : "_BLOCK_v2");
-        String newCacheName = cacheName + (USE_BLOCK_CACHE.get() ? "_BLOCK_v2" : "_INDEX_v2");
-
-        if (USE_BLOCK_CACHE.get()) {
+        boolean isBlockDiskCache = blockSizeBytes > 0;
+        removeStaleFiles(cachePath + File.separator + cacheName, isBlockDiskCache ? "_INDEX_v2" : "_BLOCK_v2");
+        String newCacheName = cacheName + (isBlockDiskCache ? "_BLOCK_v2" : "_INDEX_v2");
+
+        if (isBlockDiskCache) {
             BlockDiskCacheAttributes blockAttr = new BlockDiskCacheAttributes();
             /*
@@ -218,5 +247,5 @@
                 blockAttr.setMaxKeySize(maxDiskObjects);
             }
-            blockAttr.setBlockSizeBytes(4096); // use 4k blocks
+            blockAttr.setBlockSizeBytes(blockSizeBytes);
             ret = blockAttr;
         } else {
Index: trunk/src/org/openstreetmap/josm/gui/MainInitialization.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainInitialization.java	(revision 17363)
+++ trunk/src/org/openstreetmap/josm/gui/MainInitialization.java	(revision 17364)
@@ -153,4 +153,6 @@
                 MainApplication.toolbar.control.updateUI();
                 MainApplication.contentPanePrivate.updateUI();
+                // image provider statistics
+                ImageProvider.printStatistics();
             }))
         );
Index: trunk/src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 17363)
+++ trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 17364)
@@ -954,5 +954,5 @@
                         continue;
                     }
-                    ir = getIfAvailableLocalURL(path, type);
+                    ir = getIfAvailableLocalURL(subdir + name, path, type);
                     if (ir != null) {
                         cache.put(cacheName, ir);
@@ -984,5 +984,5 @@
                     svg = getSvgUniverse().getDiagram(uri);
                 }
-                return svg == null ? null : new ImageResource(svg);
+                return svg == null ? null : new ImageResource(url, svg);
             case OTHER:
                 BufferedImage img = null;
@@ -992,5 +992,5 @@
                     Logging.log(Logging.LEVEL_WARN, "Exception while reading HTTP image:", e);
                 }
-                return img == null ? null : new ImageResource(img);
+                return img == null ? null : new ImageResource(url, img);
             default:
                 throw new AssertionError("Unsupported type: " + type);
@@ -1041,5 +1041,5 @@
                     return null;
                 }
-                return new ImageResource(svg);
+                return new ImageResource(url, svg);
             } else {
                 try {
@@ -1050,5 +1050,5 @@
                     // CHECKSTYLE.ON: LineLength
                     Image img = read(new ByteArrayInputStream(bytes), false, true);
-                    return img == null ? null : new ImageResource(img);
+                    return img == null ? null : new ImageResource(url, img);
                 } catch (IOException | UnsatisfiedLinkError e) {
                     Logging.log(Logging.LEVEL_WARN, "Exception while reading image:", e);
@@ -1125,5 +1125,5 @@
                             svg = getSvgUniverse().getDiagram(uri);
                         }
-                        return svg == null ? null : new ImageResource(svg);
+                        return svg == null ? null : new ImageResource(fullName, svg);
                     case OTHER:
                         while (size > 0) {
@@ -1138,5 +1138,5 @@
                             Logging.warn(e);
                         }
-                        return img == null ? null : new ImageResource(img);
+                        return img == null ? null : new ImageResource(fullName, img);
                     default:
                         throw new AssertionError("Unknown ImageType: "+type);
@@ -1157,29 +1157,30 @@
      * @return the requested image or null if the request failed
      */
-    private static ImageResource getIfAvailableLocalURL(URL path, ImageType type) {
+    private static ImageResource getIfAvailableLocalURL(String cacheKey, URL path, ImageType type) {
         switch (type) {
         case SVG:
-            SVGDiagram svg = null;
-            synchronized (getSvgUniverse()) {
-                try {
-                    URI uri = null;
+            return new ImageResource(cacheKey, () -> {
+                synchronized (getSvgUniverse()) {
                     try {
-                        uri = getSvgUniverse().loadSVG(path);
-                    } catch (InvalidPathException e) {
-                        Logging.error("Cannot open {0}: {1}", path, e.getMessage());
-                        Logging.trace(e);
+                        URI uri = null;
+                        try {
+                            uri = getSvgUniverse().loadSVG(path);
+                        } catch (InvalidPathException e) {
+                            Logging.error("Cannot open {0}: {1}", path, e.getMessage());
+                            Logging.trace(e);
+                        }
+                        if (uri == null && "jar".equals(path.getProtocol())) {
+                            URL betterPath = Utils.betterJarUrl(path);
+                            if (betterPath != null) {
+                                uri = getSvgUniverse().loadSVG(betterPath);
+                            }
+                        }
+                        return getSvgUniverse().getDiagram(uri);
+                    } catch (SecurityException | IOException e) {
+                        Logging.log(Logging.LEVEL_WARN, "Unable to read SVG", e);
                     }
-                    if (uri == null && "jar".equals(path.getProtocol())) {
-                        URL betterPath = Utils.betterJarUrl(path);
-                        if (betterPath != null) {
-                            uri = getSvgUniverse().loadSVG(betterPath);
-                        }
-                    }
-                    svg = getSvgUniverse().getDiagram(uri);
-                } catch (SecurityException | IOException e) {
-                    Logging.log(Logging.LEVEL_WARN, "Unable to read SVG", e);
-                }
-            }
-            return svg == null ? null : new ImageResource(svg);
+                }
+                return null;
+            });
         case OTHER:
             BufferedImage img = null;
@@ -1196,5 +1197,5 @@
                 Logging.debug(e);
             }
-            return img == null ? null : new ImageResource(img);
+            return img == null ? null : new ImageResource(path.toString(), img);
         default:
             throw new AssertionError();
@@ -1394,5 +1395,5 @@
      */
     public static Image createBoundedImage(Image img, int maxSize) {
-        return new ImageResource(img).getImageIconBounded(new Dimension(maxSize, maxSize)).getImage();
+        return new ImageResource(img.toString(), img).getImageIconBounded(new Dimension(maxSize, maxSize)).getImage();
     }
 
@@ -1933,4 +1934,12 @@
     }
 
+    /**
+     * Prints statistics concerning the image loading caches.
+     */
+    public static void printStatistics() {
+        Logging.info(getSvgUniverse().statistics());
+        Logging.info(ImageResource.statistics());
+    }
+
     @Override
     public String toString() {
Index: trunk/src/org/openstreetmap/josm/tools/ImageResource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageResource.java	(revision 17363)
+++ trunk/src/org/openstreetmap/josm/tools/ImageResource.java	(revision 17364)
@@ -5,7 +5,10 @@
 import java.awt.Image;
 import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.function.Supplier;
 
 import javax.swing.AbstractAction;
@@ -15,4 +18,8 @@
 import javax.swing.JPanel;
 import javax.swing.UIManager;
+
+import org.apache.commons.jcs3.access.behavior.ICacheAccess;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
+import org.openstreetmap.josm.data.cache.JCSCacheManager;
 
 import com.kitfox.svg.SVGDiagram;
@@ -29,7 +36,10 @@
 
     /**
-     * Caches the image data for resized versions of the same image. The key is obtained using {@link ImageResizeMode#cacheKey(Dimension)}.
-     */
-    private final Map<Integer, BufferedImage> imgCache = new ConcurrentHashMap<>(4);
+     * Caches the image data for resized versions of the same image.
+     * Depending on {@link JCSCacheManager#USE_IMAGE_RESOURCE_CACHE}, a combined memory/disk, or an in-memory-cache is used.
+     */
+    private static final ICacheAccess<String, BufferedImageCacheEntry> imgCache = JCSCacheManager.getImageResourceCache();
+
+    private final String cacheKey;
     /**
      * SVG diagram information in case of SVG vector image.
@@ -37,4 +47,8 @@
     private SVGDiagram svg;
     /**
+     * Supplier for SVG diagram information in case of possibly cached SVG vector image.
+     */
+    private Supplier<SVGDiagram> svgSupplier;
+    /**
      * Use this dimension to request original file dimension.
      */
@@ -55,18 +69,25 @@
     /**
      * Constructs a new {@code ImageResource} from an image.
+     * @param cacheKey the caching identifier of the image
      * @param img the image
      */
-    public ImageResource(Image img) {
-        CheckParameterUtil.ensureParameterNotNull(img);
-        baseImage = img;
+    public ImageResource(String cacheKey, Image img) {
+        this.cacheKey = Objects.requireNonNull(cacheKey);
+        this.baseImage = Objects.requireNonNull(img);
     }
 
     /**
      * Constructs a new {@code ImageResource} from SVG data.
+     * @param cacheKey the caching identifier of the image
      * @param svg SVG data
      */
-    public ImageResource(SVGDiagram svg) {
-        CheckParameterUtil.ensureParameterNotNull(svg);
-        this.svg = svg;
+    public ImageResource(String cacheKey, SVGDiagram svg) {
+        this.cacheKey = Objects.requireNonNull(cacheKey);
+        this.svg = Objects.requireNonNull(svg);
+    }
+
+    public ImageResource(String cacheKey, Supplier<SVGDiagram> svgSupplier) {
+        this.cacheKey = Objects.requireNonNull(cacheKey);
+        this.svgSupplier = Objects.requireNonNull(svgSupplier);
     }
 
@@ -78,5 +99,7 @@
      */
     public ImageResource(ImageResource res, List<ImageOverlay> overlayInfo) {
+        this.cacheKey = res.cacheKey;
         this.svg = res.svg;
+        this.svgSupplier = res.svgSupplier;
         this.baseImage = res.baseImage;
         this.overlayInfo = overlayInfo;
@@ -157,4 +180,17 @@
     ImageIcon getImageIcon(Dimension dim, boolean multiResolution, ImageResizeMode resizeMode) {
         return getImageIconAlreadyScaled(GuiSizesHelper.getDimensionDpiAdjusted(dim), multiResolution, false, resizeMode);
+    }
+
+    private BufferedImage getImageFromCache(String cacheKey) {
+        try {
+            BufferedImageCacheEntry image = imgCache.get(cacheKey);
+            if (image == null || image.getImage() == null) {
+                return null;
+            }
+            Logging.trace("{0} is in cache :-)", cacheKey);
+            return image.getImage();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
     }
 
@@ -181,7 +217,13 @@
             resizeMode = ImageResizeMode.BOUNDED;
         }
-        final int cacheKey = resizeMode.cacheKey(dim);
-        BufferedImage img = imgCache.get(cacheKey);
+        final String cacheKey = String.format(Locale.ROOT, "%s--%s--%d--%d",
+                this.cacheKey, resizeMode.name(), dim.width, dim.height);
+        BufferedImage img = getImageFromCache(cacheKey);
         if (img == null) {
+            if (svgSupplier != null) {
+                svg = svgSupplier.get();
+                Logging.trace("{0} is not in cache :-(", cacheKey);
+                svgSupplier = null;
+            }
             if (svg != null) {
                 img = ImageProvider.createImageFromSvg(svg, dim, resizeMode);
@@ -211,5 +253,10 @@
                 disabledIcon.paintIcon(new JPanel(), img.getGraphics(), 0, 0);
             }
-            imgCache.put(cacheKey, img);
+            if (img == null) {
+                return null;
+            }
+            BufferedImageCacheEntry cacheEntry = BufferedImageCacheEntry.pngEncoded(img);
+            Logging.trace("Storing {0} ({1} bytes) in cache...", cacheKey, cacheEntry.getContent().length);
+            imgCache.put(cacheKey, cacheEntry);
         }
 
@@ -251,4 +298,8 @@
     }
 
+    static String statistics() {
+        return String.format("ImageResource cache: [%s]", imgCache.getStatistics());
+    }
+
     @Override
     public String toString() {
