Index: applications/editors/josm/plugins/wmsplugin/src/wmsplugin/Cache.java
===================================================================
--- applications/editors/josm/plugins/wmsplugin/src/wmsplugin/Cache.java	(revision 13645)
+++ applications/editors/josm/plugins/wmsplugin/src/wmsplugin/Cache.java	(revision 13645)
@@ -0,0 +1,136 @@
+package wmsplugin;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeMap;
+import javax.imageio.*;
+
+import org.openstreetmap.josm.Main;
+
+public class Cache {
+    final File dir;
+    // Last 5 days
+    private final long expire = Main.pref.getInteger("wmsplugin.cache.expire", 1000*60*60*24*5);
+    // 40 MBytes
+    private final long maxsize = Main.pref.getInteger("wmsplugin.cache.size", 1000*1000*40);
+    private boolean disabled = false;
+    // If the cache is full, we don't want to delete just one file
+    private final int cleanUpThreshold = 20;
+
+    Cache() {
+        this(Main.pref.get("wmsplugin.cache.path", WMSPlugin.getPrefsPath() + "cache"));
+    }
+
+    Cache(String working_dir) {
+        // Override default working directory
+        this.dir = new File(working_dir);
+        try {
+            this.dir.mkdirs();
+        } catch(Exception e) {
+            // We probably won't be able to do anything anyway
+            disabled = true;
+        }
+
+        if(expire <= 0 || maxsize <= 0)
+            disabled = true;
+    }
+
+    public BufferedImage getImg(String ident) {
+        if(disabled) return null;
+        try {
+            File img = getPath(ident);
+            if(!img.exists()) {
+                //System.out.println("Miss");
+                return null;
+            }
+            if(img.lastModified() < (new Date().getTime() - expire)) {
+                img.delete();
+                //System.out.println("Miss");
+                return null;
+            }
+            //System.out.println("Hit");
+            return ImageIO.read(img);
+        } catch(Exception e) {
+            System.out.println(e.getMessage());
+        }
+        //System.out.println("Miss");
+        return null;
+    }
+
+    public void saveImg(String ident, BufferedImage image) {
+        if(disabled) return;
+        try {
+            ImageIO.write(image, "png", getPath(ident));
+        } catch(Exception e){
+            System.out.println(e.getMessage());
+        }
+
+        // Clean up must be called manually
+    }
+
+    public BufferedImage saveImg(String ident, BufferedImage image, boolean passThrough) {
+        saveImg(ident, image);
+        return image;
+    }
+
+    public void cleanUp() {
+        if(disabled) return;
+
+        TreeMap<Long, File> modtime = new TreeMap<Long, File>();
+        long time = new Date().getTime() - expire;
+        long dirsize = 0;
+
+        for(File f : getFiles()) {
+            if(f.lastModified() < time)
+                f.delete();
+            else {
+                dirsize += f.length();
+                modtime.put(f.lastModified(), f);
+            }
+        }
+
+        if(dirsize < maxsize) return;
+
+        Set keySet = modtime.keySet();
+        Iterator it = keySet.iterator();
+        int i=0;
+        while (it.hasNext()) {
+            i++;
+            ((File)modtime.get(it.next())).delete();
+
+            // Delete a couple of files, then check again
+            if(i % cleanUpThreshold == 0 && getDirSize() < maxsize)
+                return;
+        }
+    }
+
+    private long getDirSize() {
+        long dirsize = 0;
+
+        for(File f : getFiles())
+            dirsize += f.length();
+        return dirsize;
+    }
+
+    private File[] getFiles() {
+        return dir.listFiles(
+            new FileFilter() {
+                public boolean accept(File file) {
+                    return file.getName().endsWith(".png");
+                }
+            }
+        );
+    }
+
+    private String clean(String ident) {
+        return ident.replaceAll("[^a-zA-Z0-9]", "");
+    }
+
+    private File getPath(String ident) {
+        return new File(dir, clean(ident) + ".png");
+    }
+}
Index: applications/editors/josm/plugins/wmsplugin/src/wmsplugin/GeorefImage.java
===================================================================
--- applications/editors/josm/plugins/wmsplugin/src/wmsplugin/GeorefImage.java	(revision 13637)
+++ applications/editors/josm/plugins/wmsplugin/src/wmsplugin/GeorefImage.java	(revision 13645)
@@ -1,4 +1,5 @@
 package wmsplugin;
 
+import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Point;
@@ -16,4 +17,6 @@
 public class GeorefImage implements Serializable {
     public BufferedImage image = null;
+    private BufferedImage reImg = null;
+    private Dimension reImgHash = new Dimension(0, 0);
     public EastNorth min, max;
     public boolean downloadingStarted;
@@ -44,14 +47,35 @@
         Point minPt = nc.getPoint(mi), maxPt = nc.getPoint(ma);
 
+        // downloadAndPaintVisible in WMSLayer.java requests visible images only
+        // so this path is never hit. 
         /* this is isVisible() but taking dx, dy into account */
-        if(!(g.hitClip(minPt.x, maxPt.y,
-                maxPt.x - minPt.x, minPt.y - maxPt.y)))
+        /*if(!(g.hitClip(minPt.x, maxPt.y, maxPt.x - minPt.x, minPt.y - maxPt.y))) {
             return false;
+        }*/
+        
+        // Width and height flicker about 2 pixels due to rounding errors, typically only 1
+        int width = Math.abs(maxPt.x-minPt.x);
+        int height = Math.abs(minPt.y-maxPt.y);
+        int diffx = reImgHash.width - width;
+        int diffy = reImgHash.height - height;
+        
+        // We still need to re-render if the requested size is larger (otherwise we'll have black lines)
+        // If it's only up to two pixels smaller, just draw the old image, the errors are minimal
+        // but the performance improvements when moving are huge
+        // Zooming is still slow because the images need to be resized
+        if(diffx >= 0 && diffx <= 2 && diffy >= 0 && diffy <= 2 && reImg != null) {
+            g.drawImage(reImg, minPt.x, maxPt.y, null);
+            return true;
+        }
 
-        g.drawImage(image,
-            minPt.x, maxPt.y, maxPt.x, minPt.y, // dest
+        // We haven't got a saved resized copy, so resize and cache it        
+        reImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        reImg.getGraphics().drawImage(image,
+            0, 0, width, height, // dest
             0, 0, image.getWidth(), image.getHeight(), // src
             null);
 
+        reImgHash.setSize(width, height);        
+        g.drawImage(reImg, minPt.x, maxPt.y, null);
         return true;
     }
Index: applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSGrabber.java
===================================================================
--- applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSGrabber.java	(revision 13637)
+++ applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSGrabber.java	(revision 13645)
@@ -31,4 +31,5 @@
 public class WMSGrabber extends Grabber {
     protected String baseURL;
+    protected Cache cache = new wmsplugin.Cache();
     private static Boolean shownWarning = false;
 
@@ -112,4 +113,7 @@
 
     protected BufferedImage grab(URL url) throws IOException {
+        BufferedImage cached = cache.getImg(url.toString());
+        if(cached != null) return cached;
+    
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 
@@ -123,5 +127,6 @@
         BufferedImage img = ImageIO.read(is);
         is.close();
-        return img;
+        
+        return cache.saveImg(url.toString(), img, true);
     }
 
Index: applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSInfo.java
===================================================================
--- applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSInfo.java	(revision 13637)
+++ applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSInfo.java	(revision 13645)
@@ -8,5 +8,5 @@
  * @author Frederik Ramm <frederik@remote.org>
  */
-public class WMSInfo implements Comparable {
+public class WMSInfo implements Comparable<WMSInfo> {
 
     String name;
@@ -23,16 +23,12 @@
         Main.pref.put("wmsplugin.url." + prefid + ".url", url);
     }
-    public int compareTo(Object c)
+    public int compareTo(WMSInfo c)
     {
-        Integer i = 0;
-        if(c instanceof WMSInfo)
-        {
-            WMSInfo in = (WMSInfo)c;
-            i = name.compareTo(in.name);
-            if(i == 0)
-                i = url.compareTo(in.url);
-            if(i == 0)
-                i = prefid-in.prefid;
-        }
+        WMSInfo in = (WMSInfo)c;
+        Integer i = name.compareTo(in.name);
+        if(i == 0)
+            i = url.compareTo(in.url);
+        if(i == 0)
+            i = prefid-in.prefid;
         return i;
     }
Index: applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java
===================================================================
--- applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java	(revision 13637)
+++ applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSLayer.java	(revision 13645)
@@ -177,4 +177,6 @@
             }
         }
+
+        new wmsplugin.Cache().cleanUp();
     }
 
Index: applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java
===================================================================
--- applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java	(revision 13637)
+++ applications/editors/josm/plugins/wmsplugin/src/wmsplugin/WMSPlugin.java	(revision 13645)
@@ -31,10 +31,4 @@
 import org.openstreetmap.josm.gui.MapView;
 
-
-// NW 151006 only add the landsat task when the map frame is initialised with
-// data.
-
-
-
 public class WMSPlugin extends Plugin {
 
@@ -66,8 +60,8 @@
     public void copy(String from, String to) throws FileNotFoundException, IOException
     {
-        File pluginDir = new File(Main.pref.getPreferencesDir() + "plugins/wmsplugin/");
+        File pluginDir = new File(getPrefsPath());
         if (!pluginDir.exists())
             pluginDir.mkdirs();
-        FileOutputStream out = new FileOutputStream(Main.pref.getPreferencesDir() + "plugins/wmsplugin/" + to);
+        FileOutputStream out = new FileOutputStream(getPrefsPath() + to);
         InputStream in = WMSPlugin.class.getResourceAsStream(from);
         byte[] buffer = new byte[8192];
@@ -222,3 +216,8 @@
         return new WMSPreferenceEditor();
     }
+
+    static public String getPrefsPath()
+    {
+        return Main.pref.getPluginsDirFile().getPath() + "wmsplugin/";
+    }
 }
Index: applications/editors/josm/plugins/wmsplugin/src/wmsplugin/YAHOOGrabber.java
===================================================================
--- applications/editors/josm/plugins/wmsplugin/src/wmsplugin/YAHOOGrabber.java	(revision 13637)
+++ applications/editors/josm/plugins/wmsplugin/src/wmsplugin/YAHOOGrabber.java	(revision 13645)
@@ -21,9 +21,9 @@
 public class YAHOOGrabber extends WMSGrabber{
     protected String browserCmd;
+    protected Cache cache = new wmsplugin.Cache();
 
     YAHOOGrabber(String baseURL, Bounds b, Projection proj,
             double pixelPerDegree, GeorefImage image, MapView mv, WMSLayer layer) {
-        super("file:///" + Main.pref.getPreferencesDir() + "plugins/wmsplugin/ymap.html?"
-//                + "request=getmap&format=image/jpeg"
+        super("file:///" + WMSPlugin.getPrefsPath() + "ymap.html?"
         , b, proj, pixelPerDegree, image, mv, layer);
         this.browserCmd = baseURL.replaceFirst("yahoo://", "");
@@ -31,9 +31,13 @@
 
     protected BufferedImage grab(URL url) throws IOException {
-        ArrayList<String> cmdParams = new ArrayList<String>();
         String urlstring = url.toExternalForm();
         // work around a problem in URL removing 2 slashes
         if(!urlstring.startsWith("file:///"))
             urlstring = urlstring.replaceFirst("file:", "file://");
+
+        BufferedImage cached = cache.getImg(urlstring);
+        if(cached != null) return cached;
+
+        ArrayList<String> cmdParams = new ArrayList<String>();
         StringTokenizer st = new StringTokenizer(MessageFormat.format(browserCmd, urlstring));
         while( st.hasMoreTokens() )
@@ -50,5 +54,5 @@
         }
 
-        return ImageIO.read(browser.getInputStream());
+        return cache.saveImg(urlstring, ImageIO.read(browser.getInputStream()), true);
     }
 }
