Index: /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 10819)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 10820)
@@ -35,7 +35,12 @@
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
@@ -958,4 +963,8 @@
     }
 
+    private Tile getOrCreateTile(TilePosition tilePosition) {
+        return getOrCreateTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom());
+    }
+
     private Tile getOrCreateTile(int x, int y, int zoom) {
         Tile tile = getTile(x, y, zoom);
@@ -969,4 +978,8 @@
         }
         return tile;
+    }
+
+    private Tile getTile(TilePosition tilePosition) {
+        return getTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom());
     }
 
@@ -1125,4 +1138,23 @@
             ((Graphics2D) g).fill(target);
         }
+    }
+
+    private List<Tile> paintTileImages(Graphics g, TileSet ts) {
+        Object paintMutex = new Object();
+        List<TilePosition> missed = Collections.synchronizedList(new ArrayList<>());
+        ts.visitTiles(tile -> {
+            Image img = getLoadedTileImage(tile);
+            if (img == null) {
+                missed.add(new TilePosition(tile));
+            }
+            img = applyImageProcessors((BufferedImage) img);
+            Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile);
+            synchronized (paintMutex) {
+                //cannot paint in parallel
+                drawImageInside(g, img, sourceRect, null);
+            }
+        }, missed::add);
+
+        return missed.stream().map(this::getOrCreateTile).collect(Collectors.toList());
     }
 
@@ -1138,8 +1170,5 @@
     private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
         if (zoom <= 0) return Collections.emptyList();
-        Rectangle2D borderRect = null;
-        if (border != null) {
-            borderRect = coordinateConverter.getRectangleForTile(border);
-        }
+        Rectangle2D borderRect = coordinateConverter.getRectangleForTile(border);
         List<Tile> missedTiles = new LinkedList<>();
         // The callers of this code *require* that we return any tiles
@@ -1295,4 +1324,65 @@
             return xSpan * ySpan;
         }
+
+        /**
+         * Gets a stream of all tile positions in this set
+         * @return A stream of all positions
+         */
+        public Stream<TilePosition> tilePositions() {
+            if (zoom == 0) {
+                return Stream.empty();
+            } else {
+                return IntStream.rangeClosed(minX, maxX).mapToObj(
+                        x -> IntStream.rangeClosed(minY, maxY).mapToObj(y -> new TilePosition(x, y, zoom))
+                        ).flatMap(Function.identity());
+            }
+        }
+    }
+
+    /**
+     * The position of a single tile.
+     * @author Michael Zangl
+     * @since xxx
+     */
+    private static class TilePosition {
+        private final int x;
+        private final int y;
+        private final int zoom;
+        TilePosition(int x, int y, int zoom) {
+            super();
+            this.x = x;
+            this.y = y;
+            this.zoom = zoom;
+        }
+
+        TilePosition(Tile tile) {
+            this(tile.getXtile(), tile.getYtile(), tile.getZoom());
+        }
+
+        /**
+         * @return the x position
+         */
+        public int getX() {
+            return x;
+        }
+
+        /**
+         * @return the y position
+         */
+        public int getY() {
+            return y;
+        }
+
+        /**
+         * @return the zoom
+         */
+        public int getZoom() {
+            return zoom;
+        }
+
+        @Override
+        public String toString() {
+            return "TilePosition [x=" + x + ", y=" + y + ", zoom=" + zoom + "]";
+        }
     }
 
@@ -1308,5 +1398,5 @@
          */
         private TileSet() {
-            return;
+            // default
         }
 
@@ -1338,44 +1428,31 @@
         }
 
-        /*
-         * Get all tiles represented by this TileSet that are
-         * already in the tileCache.
+        /**
+         * Get all tiles represented by this TileSet that are already in the tileCache.
          */
         private List<Tile> allExistingTiles() {
-            return this.findAllTiles(false);
+            return allTiles(p -> getTile(p));
         }
 
         private List<Tile> allTilesCreate() {
-            return this.findAllTiles(true);
-        }
-
-        private List<Tile> findAllTiles(boolean create) {
-            // Tileset is either empty or too large
-            if (zoom == 0 || this.insane())
-                return Collections.emptyList();
-            List<Tile> ret = new ArrayList<>();
-            for (int x = minX; x <= maxX; x++) {
-                for (int y = minY; y <= maxY; y++) {
-                    Tile t;
-                    if (create) {
-                        t = getOrCreateTile(x, y, zoom);
-                    } else {
-                        t = getTile(x, y, zoom);
-                    }
-                    if (t != null) {
-                        ret.add(t);
-                    }
-                }
-            }
-            return ret;
+            return allTiles(p -> getOrCreateTile(p));
+        }
+
+        private List<Tile> allTiles(Function<TilePosition, Tile> mapper) {
+            return tilePositions().map(mapper).filter(Objects::nonNull).collect(Collectors.toList());
+        }
+
+        @Override
+        public Stream<TilePosition> tilePositions() {
+            if (this.insane()) {
+                // Tileset is either empty or too large
+                return Stream.empty();
+            } else {
+                return super.tilePositions();
+            }
         }
 
         private List<Tile> allLoadedTiles() {
-            List<Tile> ret = new ArrayList<>();
-            for (Tile t : this.allExistingTiles()) {
-                if (t.isLoaded())
-                    ret.add(t);
-            }
-            return ret;
+            return allExistingTiles().stream().filter(Tile::isLoaded).collect(Collectors.toList());
         }
 
@@ -1386,16 +1463,5 @@
             final int centerX = (int) Math.ceil((minX + maxX) / 2d);
             final int centerY = (int) Math.ceil((minY + maxY) / 2d);
-            return new Comparator<Tile>() {
-                private int getDistance(Tile t) {
-                    return Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY);
-                }
-
-                @Override
-                public int compare(Tile o1, Tile o2) {
-                    int distance1 = getDistance(o1);
-                    int distance2 = getDistance(o2);
-                    return Integer.compare(distance1, distance2);
-                }
-            };
+            return Comparator.comparingInt(t -> Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY));
         }
 
@@ -1417,4 +1483,24 @@
                     tileLoader.createTileLoaderJob(t).submit(force);
                 }
+            }
+        }
+
+        /**
+         * Call the given paint method for all tiles in this tile set.
+         * <p>
+         * Uses a parallel stream.
+         * @param visitor A visitor to call for each tile.
+         * @param missed a consumer to call for each missed tile.
+         */
+        public void visitTiles(Consumer<Tile> visitor, Consumer<TilePosition> missed) {
+            tilePositions().parallel().forEach(tp -> visitTilePosition(visitor, tp, missed));
+        }
+
+        private void visitTilePosition(Consumer<Tile> visitor, TilePosition tp, Consumer<TilePosition> missed) {
+            Tile tile = getTile(tp);
+            if (tile == null) {
+                missed.accept(tp);
+            } else {
+                visitor.accept(tile);
             }
         }
@@ -1592,5 +1678,5 @@
         g.setColor(Color.DARK_GRAY);
 
-        List<Tile> missedTiles = this.paintTileImages(g, ts, displayZoomLevel, null);
+        List<Tile> missedTiles = this.paintTileImages(g, ts);
         int[] otherZooms = {-1, 1, -2, 2, -3, -4, -5};
         for (int zoomOffset : otherZooms) {
