Index: trunk/src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 4270)
+++ trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 4271)
@@ -10,4 +10,5 @@
 import java.awt.Component;
 import java.awt.Cursor;
+import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
@@ -42,5 +43,5 @@
 
 /**
- * Helperclass to support the application with images.
+ * Helper class to support the application with images.
  * @author imi
  */
@@ -59,19 +60,5 @@
     public static enum ImageType {
         SVG,    // scalable vector graphics
-        OTHER   // everything else, e.g. png, gif
-                // must be supported by Java
-    }
-
-    /**
-     * remember whether the image has been sanitized
-     */
-    private static class ImageWrapper {
-        Image img;
-        boolean sanitized;
-
-        public ImageWrapper(Image img, boolean sanitized) {
-            this.img = img;
-            this.sanitized = sanitized;
-        }
+        OTHER   // everything else, e.g. png, gif (must be supported by Java)
     }
 
@@ -79,5 +66,5 @@
      * The icon cache
      */
-    private static Map<String, ImageWrapper> cache = new HashMap<String, ImageWrapper>();
+    private static Map<String, ImageResource> cache = new HashMap<String, ImageResource>();
 
     /**
@@ -127,4 +114,8 @@
     }
 
+    public static ImageIcon getIfAvailable(Collection<String> dirs, String id, String subdir, String name, File archive, boolean sanitize) {
+        return getIfAvailable(dirs, id, subdir, name, archive, null, sanitize);
+    }
+    
     /**
      * The full path of the image is either a url (starting with http://)
@@ -137,19 +128,19 @@
      *                  it will try both extensions.
      * @param archive   A zip file where the image is located (may be null).
+     * @param dim       The dimensions of the image if it should be scaled. null if the
+     *                  original size of the image should be returned. The width 
+     *                  part of the dimension can be -1. Then it will scale the width
+     *                  in the same way as the height. (And the other way around.)
      * @param sanitize  If the image should be repainted to a new BufferedImage to work
      *                  around certain issues.
      */
-    public static ImageIcon getIfAvailable(Collection<String> dirs, String id, String subdir, String name, File archive, boolean sanitize) {
-        ImageWrapper iw = getIfAvailableImpl(dirs, id, subdir, name, archive);
-        if (iw == null)
+    public static ImageIcon getIfAvailable(Collection<String> dirs, String id, String subdir, String name, File archive, Dimension dim, boolean sanitize) {
+        ImageResource ir = getIfAvailableImpl(dirs, id, subdir, name, archive);
+        if (ir == null)
             return null;
-        if (sanitize && !iw.sanitized) {
-            iw.img = sanitize(iw.img);
-            iw.sanitized = true;
-        }
-        return new ImageIcon(iw.img);
-    }
-
-    private static ImageWrapper getIfAvailableImpl(Collection<String> dirs, String id, String subdir, String name, File archive) {
+        return ir.getImageIcon(dim == null ? ImageResource.DEFAULT_DIMENSION : dim, sanitize);
+    }
+
+    private static ImageResource getIfAvailableImpl(Collection<String> dirs, String id, String subdir, String name, File archive) {
         if (name == null)
             return null;
@@ -158,11 +149,11 @@
         if (name.startsWith("http://")) {
             String url = name;
-            ImageWrapper iw = cache.get(url);
-            if (iw != null) return iw;
-            iw = getIfAvailableHttp(url, type);
-            if (iw != null) {
-                cache.put(url, iw);
-            }
-            return iw;
+            ImageResource ir = cache.get(url);
+            if (ir != null) return ir;
+            ir = getIfAvailableHttp(url, type);
+            if (ir != null) {
+                cache.put(url, ir);
+            }
+            return ir;
         }
 
@@ -198,14 +189,14 @@
                 }
 
-                ImageWrapper iw = cache.get(cache_name);
-                if (iw != null) return iw;
+                ImageResource ir = cache.get(cache_name);
+                if (ir != null) return ir;
 
                 switch (place) {
                     case ARCHIVE:
                         if (archive != null) {
-                            iw = getIfAvailableZip(full_name, archive, type);
-                            if (iw != null) {
-                                cache.put(cache_name, iw);
-                                return iw;
+                            ir = getIfAvailableZip(full_name, archive, type);
+                            if (ir != null) {
+                                cache.put(cache_name, ir);
+                                return ir;
                             }
                         }
@@ -220,8 +211,8 @@
                         if (path == null)
                             continue;
-                        iw = getIfAvailableLocalURL(path, type);
-                        if (iw != null) {
-                            cache.put(cache_name, iw);
-                            return iw;
+                        ir = getIfAvailableLocalURL(path, type);
+                        if (ir != null) {
+                            cache.put(cache_name, ir);
+                            return ir;
                         }
                         break;
@@ -232,6 +223,5 @@
     }
 
-    private static ImageWrapper getIfAvailableHttp(String url, ImageType type) {
-        Image img = null;
+    private static ImageResource getIfAvailableHttp(String url, ImageType type) {
         try {
             MirroredInputStream is = new MirroredInputStream(url,
@@ -240,18 +230,19 @@
                 case SVG:
                     URI uri = getSvgUniverse().loadSVG(is, is.getFile().toURI().toURL().toString());
-                    img = createImageFromSvgUri(uri);
-                    break;
+                    SVGDiagram svg = getSvgUniverse().getDiagram(uri);
+                    return svg == null ? null : new ImageResource(svg);
                 case OTHER:
-                    img = Toolkit.getDefaultToolkit().createImage(is.getFile().toURI().toURL());
-                    break;
+                    Image img = Toolkit.getDefaultToolkit().createImage(is.getFile().toURI().toURL());
+                    return img == null ? null : new ImageResource(img, false);
+                default:
+                    throw new AssertionError();
             }
         } catch (IOException e) {
-        }
-        return img == null ? null : new ImageWrapper(img, false);
-    }
-
-    private static ImageWrapper getIfAvailableZip(String full_name, File archive, ImageType type) {
+            return null;
+        }
+    }
+
+    private static ImageResource getIfAvailableZip(String full_name, File archive, ImageType type) {
         ZipFile zipFile = null;
-        Image img = null;
         try
         {
@@ -269,6 +260,6 @@
                         case SVG:
                             URI uri = getSvgUniverse().loadSVG(is, full_name);
-                            img = createImageFromSvgUri(uri);
-                            break;
+                            SVGDiagram svg = getSvgUniverse().getDiagram(uri);
+                            return svg == null ? null : new ImageResource(svg);
                         case OTHER:
                             while(size > 0)
@@ -278,6 +269,8 @@
                                 size -= l;
                             }
-                            img = Toolkit.getDefaultToolkit().createImage(buf);
-                            break;
+                            Image img = Toolkit.getDefaultToolkit().createImage(buf);
+                            return img == null ? null : new ImageResource(img, false);
+                        default:
+                            throw new AssertionError();
                     }
                 } finally {
@@ -297,19 +290,19 @@
             }
         }
-        return img == null ? null : new ImageWrapper(img, false);
-    }
-
-    private static ImageWrapper getIfAvailableLocalURL(URL path, ImageType type) {
-        Image img = null;
+        return null;
+    }
+
+    private static ImageResource getIfAvailableLocalURL(URL path, ImageType type) {
         switch (type) {
             case SVG:
                 URI uri = getSvgUniverse().loadSVG(path);
-                img = createImageFromSvgUri(uri);
-                break;
+                SVGDiagram svg = getSvgUniverse().getDiagram(uri);
+                return svg == null ? null : new ImageResource(svg);
             case OTHER:
-                img = Toolkit.getDefaultToolkit().createImage(path);
-                break;
-        }
-        return img == null ? null : new ImageWrapper(img, false);
+                Image img = Toolkit.getDefaultToolkit().createImage(path);
+                return img == null ? null : new ImageResource(img, false);
+            default:
+                throw new AssertionError();
+        }
     }
 
@@ -521,14 +514,34 @@
     }
 
-    private static Image createImageFromSvgUri(URI uri) {
-        SVGDiagram dia = getSvgUniverse().getDiagram(uri);
-        int w = (int)dia.getWidth();
-        int h = (int)dia.getHeight();
-        Image img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+    public static Image createImageFromSvg(SVGDiagram svg, Dimension dim) {
+        float realWidth = svg.getWidth();
+        float realHeight = svg.getHeight();
+        int width = Math.round(realWidth);
+        int height = Math.round(realHeight);
+        Double scaleX = null, scaleY = null;
+        if (dim.width != -1) {
+            width = dim.width;
+            scaleX = (double) width / realWidth;
+            if (dim.height == -1) {
+                scaleY = scaleX;
+                height = (int) Math.round(realHeight * scaleY);
+            } else {
+                height = dim.height;
+                scaleY = (double) height / realHeight;
+            }
+        } else if (dim.height != -1) {
+            height = dim.height;
+            scaleX = scaleY = (double) height / realHeight;
+            width = (int) Math.round(realWidth * scaleX);
+        }
+        Image img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = ((BufferedImage) img).createGraphics();
-        g.setClip(0, 0, w, h);
+        g.setClip(0, 0, width, height);
+        if (scaleX != null) {
+            g.scale(scaleX, scaleY);
+        }
         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
         try {
-            dia.render(g);
+            svg.render(g);
         } catch (SVGException ex) {
             return null;
Index: trunk/src/org/openstreetmap/josm/tools/ImageRequest.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageRequest.java	(revision 4271)
+++ trunk/src/org/openstreetmap/josm/tools/ImageRequest.java	(revision 4271)
@@ -0,0 +1,86 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+import java.io.File;
+import java.util.Collection;
+import javax.swing.ImageIcon;
+
+/**
+ * Alternative way to request an image.
+ * E.g.
+ *
+ * ImageIcon icon = new ImageRequest().setName(imgName).setWidth(100).setHeight(120).get();
+ *
+ * or in funky double-brace style
+ *
+ * ImageIcon icon = new ImageProvider.Request(){{name=imgName; width=100; height=120;}}.get();
+ */
+public class ImageRequest {
+    protected Collection<String> dirs;
+    protected String id;
+    protected String subdir;
+    protected String name;
+    protected File archive;
+    protected int width = -1;
+    protected int height = -1;
+    protected boolean sanitize;
+    protected boolean required = true;
+
+    public ImageRequest setDirs(Collection<String> dirs) {
+        this.dirs = dirs;
+        return this;
+    }
+
+    public ImageRequest setId(String id) {
+        this.id = id;
+        return this;
+    }
+
+    public ImageRequest setSubdir(String subdir) {
+        this.subdir = subdir;
+        return this;
+    }
+
+    public ImageRequest setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    public ImageRequest setArchive(File archive) {
+        this.archive = archive;
+        return this;
+    }
+
+    public ImageRequest setWidth(int width) {
+        this.width = width;
+        return this;
+    }
+
+    public ImageRequest setHeight(int height) {
+        this.height = height;
+        return this;
+    }
+
+    public ImageRequest setSanitize(boolean sanitize) {
+        this.sanitize = sanitize;
+        return this;
+    }
+
+    public ImageRequest setRequired(boolean required) {
+        this.required = required;
+        return this;
+    }
+
+    public ImageIcon get() {
+        ImageIcon icon = ImageProvider.getIfAvailable(dirs, id, subdir, name, archive, new Dimension(width, height), sanitize);
+        if (required && icon == null) {
+            String ext = name.indexOf('.') != -1 ? "" : ".???";
+            throw new NullPointerException(tr("Fatal: failed to locate image ''{0}''. This is a serious configuration problem. JOSM will stop working.", name + ext));
+        }
+        return icon;
+    }
+    
+}
Index: trunk/src/org/openstreetmap/josm/tools/ImageResource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageResource.java	(revision 4271)
+++ trunk/src/org/openstreetmap/josm/tools/ImageResource.java	(revision 4271)
@@ -0,0 +1,96 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import com.kitfox.svg.SVGDiagram;
+
+import java.awt.Dimension;
+import java.awt.Image;
+import java.util.HashMap;
+import javax.swing.ImageIcon;
+
+/**
+ * Holds data for one particular image.
+ * It can be backed by a svg or raster image.
+ * 
+ * In the first case, 'svg' is not null and in the latter case, 'imgCache' has 
+ * at least one entry for the key DEFAULT_DIMENSION.
+ */
+class ImageResource {
+    
+    /**
+     * Caches the image data for resized versions of the same image.
+     */
+    private HashMap<Dimension, ImageWrapper> imgCache = new HashMap<Dimension, ImageWrapper>();
+    private SVGDiagram svg;
+    public static final Dimension DEFAULT_DIMENSION = new Dimension(-1, -1);
+ 
+    /**
+     * remember whether the image has been sanitized
+     */
+    private static class ImageWrapper {
+        Image img;
+        boolean sanitized;
+
+        public ImageWrapper(Image img, boolean sanitized) {
+            CheckParameterUtil.ensureParameterNotNull(img);
+            this.img = img;
+            this.sanitized = sanitized;
+        }
+    }
+    
+    public ImageResource(Image img, boolean sanitized) {
+        CheckParameterUtil.ensureParameterNotNull(img);
+        imgCache.put(DEFAULT_DIMENSION, new ImageWrapper(img, sanitized));
+    }
+
+    public ImageResource(SVGDiagram svg) {
+        CheckParameterUtil.ensureParameterNotNull(svg);
+        this.svg = svg;
+    }
+
+    public ImageIcon getImageIcon() {
+        return getImageIcon(DEFAULT_DIMENSION, false);
+    }
+
+    /**
+     * Get an ImageIcon object for the image of this resource
+     * @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 sanitized Whether the returned image should be copied to a BufferedImage
+     *          to avoid certain problem with native image formats.
+     */
+    public ImageIcon getImageIcon(Dimension dim, boolean sanitized) {
+        ImageWrapper iw = imgCache.get(dim);
+        if (iw != null) {
+            if (sanitized && !iw.sanitized) {
+                iw.img = ImageProvider.sanitize(iw.img);
+                iw.sanitized = true;
+            }
+            return new ImageIcon(iw.img);
+        }
+        if (svg != null) {
+            Image img = ImageProvider.createImageFromSvg(svg, dim);
+            imgCache.put(dim, new ImageWrapper(img, true));
+            return new ImageIcon(img);
+        } else {
+            ImageWrapper base = imgCache.get(DEFAULT_DIMENSION);
+            if (base == null) throw new AssertionError();
+            
+            int width = dim.width;
+            int height = dim.height;
+            ImageIcon icon = new ImageIcon(base.img);
+            if (width == -1) {
+                width = icon.getIconWidth() * height / icon.getIconHeight();
+            } else if (height == -1) {
+                height = icon.getIconHeight() * width / icon.getIconWidth();
+            }
+            Image img = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
+            if (sanitized) {
+                img = ImageProvider.sanitize(img);
+            }
+            imgCache.put(dim, new ImageWrapper(img, sanitized));
+            return new ImageIcon(img);
+        }
+    }
+}
