Ticket #11487: 11487.patch

File 11487.patch, 7.2 KB (added by taylor.smock, 19 months ago)

Proof of Concept -- DO NOT APPLY -- see comment:2 for details

  • src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    Subject: [PATCH] #11487: Try to improve render performance
    ---
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java b/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
    a b  
    1111import java.awt.Composite;
    1212import java.awt.Graphics2D;
    1313import java.awt.GridBagLayout;
     14import java.awt.Point;
    1415import java.awt.Rectangle;
    1516import java.awt.TexturePaint;
    1617import java.awt.datatransfer.Transferable;
    1718import java.awt.datatransfer.UnsupportedFlavorException;
    1819import java.awt.event.ActionEvent;
     20import java.awt.geom.AffineTransform;
    1921import java.awt.geom.Area;
    2022import java.awt.geom.Path2D;
    2123import java.awt.geom.Rectangle2D;
     
    4951import javax.swing.JPanel;
    5052import javax.swing.JScrollPane;
    5153
     54import org.apache.commons.jcs3.access.CacheAccess;
     55import org.openstreetmap.gui.jmapviewer.OsmMercator;
     56import org.openstreetmap.gui.jmapviewer.TileXY;
    5257import org.openstreetmap.josm.actions.AutoScaleAction;
    5358import org.openstreetmap.josm.actions.ExpertToggleAction;
    5459import org.openstreetmap.josm.actions.RenameLayerAction;
     
    5863import org.openstreetmap.josm.data.Data;
    5964import org.openstreetmap.josm.data.ProjectionBounds;
    6065import org.openstreetmap.josm.data.UndoRedoHandler;
     66import org.openstreetmap.josm.data.cache.JCSCacheManager;
    6167import org.openstreetmap.josm.data.conflict.Conflict;
    6268import org.openstreetmap.josm.data.conflict.ConflictCollection;
    6369import org.openstreetmap.josm.data.coor.EastNorth;
     
    155161    private boolean requiresUploadToServer;
    156162    /** Flag used to know if the layer is being uploaded */
    157163    private final AtomicBoolean isUploadInProgress = new AtomicBoolean(false);
     164    /** A cache used for painting */
     165    private final CacheAccess<String, BufferedImage> cache = JCSCacheManager.getCache("osmDataLayer:" + this);
     166    /** The last zoom that was painted (used to invalidate {@link #cache}) */
     167    private int lastZoom;
     168    /** The map paint index that was painted (used to invalidate {@link #cache}) */
     169    private int lastDataIdx;
    158170
    159171    /**
    160172     * List of validation errors in this layer.
     
    540552        AbstractMapRenderer painter = MapRendererFactory.getInstance().createActiveRenderer(g, mv, inactive);
    541553        painter.enableSlowOperations(mv.getMapMover() == null || !mv.getMapMover().movementInProgress()
    542554                || !PROPERTY_HIDE_LABELS_WHILE_DRAGGING.get());
    543         painter.render(data, virtual, box);
     555        final double scale = mv.getScale();
     556        final int tileSize = 256;
     557        // We might have to fall back to the old method if user is reprojecting
     558        final double topResolution = 2 * Math.PI * OsmMercator.EARTH_RADIUS / tileSize;
     559        // Use to invalidate cache in the future
     560        int zoom;
     561        for (zoom = 0; zoom < 30; zoom++) {// Use something like imagery.{generic|tms}.max_zoom_lvl (20 is a bit too low for our needs)
     562            if (scale > topResolution / (Math.pow(2, zoom))) {
     563                zoom = zoom > 0 ? zoom - 1 : zoom;
     564                break;
     565            }
     566        }
     567        if (zoom != lastZoom || data.getMappaintCacheIndex() != lastDataIdx
     568                || !data.selectionEmpty()
     569                || !data.getHighlightedVirtualNodes().isEmpty()
     570                || !data.getHighlightedWaySegments().isEmpty()) {
     571            cache.clear();
     572            lastZoom = zoom;
     573            lastDataIdx = data.getMappaintCacheIndex();
     574            Logging.trace("OsmDataLayer {0} paint cache cleared", this.getName());
     575        }
     576        final List<TileXY> toRender = new ArrayList<>();
     577        final TileXY upperRight = latLonToTile(box.getMaxLat(), box.getMaxLon(), zoom);
     578        final TileXY lowerLeft = latLonToTile(box.getMinLat(), box.getMinLon(), zoom);
     579        for (int x = lowerLeft.getXIndex(); x <= upperRight.getXIndex(); x++) {
     580            for (int y = upperRight.getYIndex(); y <= lowerLeft.getYIndex(); y++) {
     581                toRender.add(new TileXY(x, y));
     582            }
     583        }
     584
     585        for (TileXY tile : toRender) {
     586            final int actualZoom = zoom;
     587            final double lat = yToLat(tile.getYIndex(), zoom);
     588            final double lon = xToLon(tile.getXIndex(), zoom);
     589            final Point point = mv.getPoint(new LatLon(lat, lon));
     590            BufferedImage tileImage = cache.get(tile.toString(),
     591                    () -> generateTile(data, mv, point, inactive, virtual, tile, tileSize, actualZoom));
     592            g.drawImage(tileImage, point.x, point.y, tileSize, tileSize, null, null);
     593        }
     594        //painter.render(data, virtual, box);
    544595        MainApplication.getMap().conflictDialog.paintConflicts(g, mv);
    545596    }
    546597
     598    private static BufferedImage generateTile(DataSet data, MapView mv, Point point, boolean inactive, boolean virtual, TileXY tile,
     599                                              int tileSize, int zoom) {
     600        BufferedImage bufferedImage = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_4BYTE_ABGR);
     601        Graphics2D g2d = bufferedImage.createGraphics();
     602        g2d.setTransform(AffineTransform.getTranslateInstance(-point.x, -point.y));
     603        try {
     604            AbstractMapRenderer tilePainter = MapRendererFactory.getInstance().createActiveRenderer(g2d, mv, inactive);
     605            tilePainter.render(data, virtual, tileToBounds(tile, zoom));
     606        } finally {
     607            g2d.dispose();
     608        }
     609        return bufferedImage;
     610    }
     611
     612    private static Bounds tileToBounds(TileXY tile, int zoom) {
     613        return new Bounds(yToLat(tile.getYIndex() + 1, zoom), xToLon(tile.getXIndex(), zoom),
     614                yToLat(tile.getYIndex(), zoom), xToLon(tile.getXIndex() + 1, zoom));
     615    }
     616
     617    private static double xToLon(int x, int zoom) {
     618        return (x / Math.pow(2, zoom)) * 360 - 180;
     619    }
     620
     621    private static double yToLat(int y, int zoom) {
     622        double t = Math.PI - (2 * Math.PI * y) / (Math.pow(2, zoom));
     623        return 180 / Math.PI * Math.atan((Math.exp(t) - Math.exp(-t)) / 2);
     624    }
     625
     626    private static TileXY latLonToTile(double lat, double lon, int zoom) {
     627        int xCoord = (int) Math.floor(Math.pow(2, zoom) * (180 + lon) / 360);
     628        int yCoord = (int) Math.floor(Math.pow(2, zoom) * (1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2);
     629        return new TileXY(xCoord, yCoord);
     630    }
     631
    547632    @Override public String getToolTipText() {
    548633        DataCountVisitor counter = new DataCountVisitor();
    549634        for (final OsmPrimitive osm : data.allPrimitives()) {
     
    11651250
    11661251    @Override
    11671252    public void processDatasetEvent(AbstractDatasetChangedEvent event) {
     1253        cache.clear();
    11681254        invalidate();
    11691255        setRequiresSaveToFile(true);
    11701256        setRequiresUploadToServer(event.getDataset().requiresUploadToServer());
     
    11721258
    11731259    @Override
    11741260    public void selectionChanged(SelectionChangeEvent event) {
     1261        cache.clear();
    11751262        invalidate();
    11761263    }
    11771264