Ticket #13222: patch-mapview-imagery-tile-source-paint-iteration.patch

File patch-mapview-imagery-tile-source-paint-iteration.patch, 10.6 KB (added by michael2402, 5 years ago)
  • src/org/openstreetmap/gui/jmapviewer/Tile.java

    diff --git a/src/org/openstreetmap/gui/jmapviewer/Tile.java b/src/org/openstreetmap/gui/jmapviewer/Tile.java
    index ba59ee0..00685f9 100644
    a b public class Tile { 
    228228    }
    229229
    230230    /**
    231      * @return tile indexes as TileXY object
     231     * @return tile indexes of the top left corner as TileXY object
    232232     */
    233233    public TileXY getTileXY() {
    234234        return new TileXY(xtile, ytile);
  • src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
    index 6e9bdc3..35e9558 100644
    a b import java.util.LinkedList; 
    3434import java.util.List;
    3535import java.util.Map;
    3636import java.util.Map.Entry;
     37import java.util.Objects;
    3738import java.util.Set;
    3839import java.util.concurrent.ConcurrentSkipListSet;
    3940import java.util.concurrent.atomic.AtomicInteger;
     41import java.util.function.Consumer;
     42import java.util.function.Function;
     43import java.util.stream.Collectors;
     44import java.util.stream.IntStream;
    4045import java.util.stream.Stream;
    4146
    4247import javax.swing.AbstractAction;
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    968973        return new Tile(tileSource, x, y, zoom);
    969974    }
    970975
     976    private Tile getOrCreateTile(TilePosition tilePosition) {
     977        return getOrCreateTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom());
     978    }
     979
    971980    private Tile getOrCreateTile(int x, int y, int zoom) {
    972981        Tile tile = getTile(x, y, zoom);
    973982        if (tile == null) {
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    981990        return tile;
    982991    }
    983992
     993    private Tile getTile(TilePosition tilePosition) {
     994        return getTile(tilePosition.getX(), tilePosition.getY(), tilePosition.getZoom());
     995    }
     996
    984997    /**
    985998     * Returns tile at given position.
    986999     * This can and will return null for tiles that are not already in the cache.
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    11211134        }
    11221135    }
    11231136
     1137    private List<Tile> paintTileImages(Graphics g, TileSet ts) {
     1138        Object paintMutex = new Object();
     1139        List<TilePosition> missed = Collections.synchronizedList(new ArrayList<>());
     1140        ts.visitTiles(tile -> {
     1141            Image img = getLoadedTileImage(tile);
     1142            if (img == null) {
     1143                missed.add(new TilePosition(tile));
     1144            }
     1145            img = applyImageProcessors((BufferedImage) img);
     1146            Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile);
     1147            synchronized (paintMutex) {
     1148                //cannot paint in parallel
     1149                drawImageInside(g, img, sourceRect, null);
     1150            }
     1151        }, missed::add);
     1152
     1153        return missed.stream().map(this::getOrCreateTile).collect(Collectors.toList());
     1154    }
     1155
    11241156    // This function is called for several zoom levels, not just
    11251157    // the current one.  It should not trigger any tiles to be
    11261158    // downloaded.  It should also avoid polluting the tile cache
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    11321164    // border is null and we draw the entire tile set.
    11331165    private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
    11341166        if (zoom <= 0) return Collections.emptyList();
    1135         Rectangle2D borderRect = null;
    1136         if (border != null) {
    1137             borderRect = coordinateConverter.getRectangleForTile(border);
    1138         }
     1167        Rectangle2D borderRect = coordinateConverter.getRectangleForTile(border);
    11391168        List<Tile> missedTiles = new LinkedList<>();
    11401169        // The callers of this code *require* that we return any tiles
    11411170        // that we do not draw in missedTiles.  ts.allExistingTiles() by
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    12861315            int ySpan = maxY - minY + 1;
    12871316            return xSpan * ySpan;
    12881317        }
     1318
     1319        /**
     1320         * Gets a stream of all tile positions in this set
     1321         * @return A stream of all positions
     1322         */
     1323        public Stream<TilePosition> tilePositions() {
     1324            if (zoom == 0) {
     1325                return Stream.empty();
     1326            } else {
     1327                return IntStream.rangeClosed(minX, maxX).mapToObj(
     1328                        x -> IntStream.rangeClosed(minY, maxY).mapToObj(y -> new TilePosition(x, y, zoom))
     1329                        ).flatMap(Function.identity());
     1330            }
     1331        }
     1332    }
     1333
     1334    /**
     1335     * The position of a single tile.
     1336     * @author Michael Zangl
     1337     * @since xxx
     1338     */
     1339    private static class TilePosition {
     1340        private final int x;
     1341        private final int y;
     1342        private final int zoom;
     1343        public TilePosition(int x, int y, int zoom) {
     1344            super();
     1345            this.x = x;
     1346            this.y = y;
     1347            this.zoom = zoom;
     1348        }
     1349
     1350        public TilePosition(Tile tile) {
     1351            this(tile.getXtile(), tile.getYtile(), tile.getZoom());
     1352        }
     1353
     1354        /**
     1355         * @return the x position
     1356         */
     1357        public int getX() {
     1358            return x;
     1359        }
     1360
     1361        /**
     1362         * @return the y position
     1363         */
     1364        public int getY() {
     1365            return y;
     1366        }
     1367
     1368        /**
     1369         * @return the zoom
     1370         */
     1371        public int getZoom() {
     1372            return zoom;
     1373        }
     1374
     1375        @Override
     1376        public String toString() {
     1377            return "TilePosition [x=" + x + ", y=" + y + ", zoom=" + zoom + "]";
     1378        }
    12891379    }
    12901380
    12911381    private class TileSet extends TileRange {
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    12991389         * null tile set
    13001390         */
    13011391        private TileSet() {
    1302             return;
     1392            // default
    13031393        }
    13041394
    13051395        protected void sanitize() {
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    13341424         * already in the tileCache.
    13351425         */
    13361426        private List<Tile> allExistingTiles() {
    1337             return this.__allTiles(false);
     1427            return allTiles(p -> getTile(p));
    13381428        }
    13391429
    13401430        private List<Tile> allTilesCreate() {
    1341             return this.__allTiles(true);
     1431            return allTiles(p -> getOrCreateTile(p));
    13421432        }
    13431433
    1344         private List<Tile> __allTiles(boolean create) {
    1345             // Tileset is either empty or too large
    1346             if (zoom == 0 || this.insane())
    1347                 return Collections.emptyList();
    1348             List<Tile> ret = new ArrayList<>();
    1349             for (int x = minX; x <= maxX; x++) {
    1350                 for (int y = minY; y <= maxY; y++) {
    1351                     Tile t;
    1352                     if (create) {
    1353                         t = getOrCreateTile(x, y, zoom);
    1354                     } else {
    1355                         t = getTile(x, y, zoom);
    1356                     }
    1357                     if (t != null) {
    1358                         ret.add(t);
    1359                     }
    1360                 }
     1434        private List<Tile> allTiles(Function<TilePosition, Tile> mapper) {
     1435            return tilePositions().map(mapper).filter(Objects::nonNull).collect(Collectors.toList());
     1436        }
     1437
     1438        @Override
     1439        public Stream<TilePosition> tilePositions() {
     1440            if (this.insane()) {
     1441                // Tileset is either empty or too large
     1442                return Stream.empty();
     1443            } else {
     1444                return super.tilePositions();
    13611445            }
    1362             return ret;
    13631446        }
    13641447
    13651448        private List<Tile> allLoadedTiles() {
    1366             List<Tile> ret = new ArrayList<>();
    1367             for (Tile t : this.allExistingTiles()) {
    1368                 if (t.isLoaded())
    1369                     ret.add(t);
    1370             }
    1371             return ret;
     1449            return allExistingTiles().stream().filter(Tile::isLoaded).collect(Collectors.toList());
    13721450        }
    13731451
    13741452        /**
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    13771455        private Comparator<Tile> getTileDistanceComparator() {
    13781456            final int centerX = (int) Math.ceil((minX + maxX) / 2d);
    13791457            final int centerY = (int) Math.ceil((minY + maxY) / 2d);
    1380             return new Comparator<Tile>() {
    1381                 private int getDistance(Tile t) {
    1382                     return Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY);
    1383                 }
    1384 
    1385                 @Override
    1386                 public int compare(Tile o1, Tile o2) {
    1387                     int distance1 = getDistance(o1);
    1388                     int distance2 = getDistance(o2);
    1389                     return Integer.compare(distance1, distance2);
    1390                 }
    1391             };
     1458            return Comparator.comparingInt(t -> Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY));
    13921459        }
    13931460
    13941461        private void loadAllTiles(boolean force) {
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    14111478            }
    14121479        }
    14131480
     1481        /**
     1482         * Call the given paint method for all tiles in this tile set.
     1483         * <p>
     1484         * Uses a parallel stream.
     1485         * @param visitor A visitor to call for each tile.
     1486         * @param missed a consumer to call for each missed tile.
     1487         */
     1488        public void visitTiles(Consumer<Tile> visitor, Consumer<TilePosition> missed) {
     1489            tilePositions().parallel().forEach(tp -> visitTilePosition(visitor, tp, missed));
     1490        }
     1491
     1492        private void visitTilePosition(Consumer<Tile> visitor, TilePosition tp, Consumer<TilePosition> missed) {
     1493            Tile tile = getTile(tp);
     1494            if (tile == null) {
     1495                missed.accept(tp);
     1496            } else {
     1497                visitor.accept(tile);
     1498            }
     1499        }
     1500
    14141501        @Override
    14151502        public String toString() {
    14161503            return getClass().getName() + ": zoom: " + zoom + " X(" + minX + ", " + maxX + ") Y(" + minY + ", " + maxY + ") size: " + size();
    implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi 
    15831670
    15841671        g.setColor(Color.DARK_GRAY);
    15851672
    1586         List<Tile> missedTiles = this.paintTileImages(g, ts, displayZoomLevel, null);
     1673        List<Tile> missedTiles = this.paintTileImages(g, ts);
    15871674        int[] otherZooms = {-1, 1, -2, 2, -3, -4, -5};
    15881675        for (int zoomOffset : otherZooms) {
    15891676            if (!getDisplaySettings().isAutoZoom()) {