Ignore:
Timestamp:
2017-04-05T11:33:25+02:00 (7 years ago)
Author:
bastiK
Message:

see #7427 - rework the way screen pixel coordinates for tiles are calculated

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java

    r11835 r11841  
    1313import java.awt.Image;
    1414import java.awt.Point;
     15import java.awt.Shape;
    1516import java.awt.Toolkit;
    1617import java.awt.event.ActionEvent;
    1718import java.awt.event.MouseAdapter;
    1819import java.awt.event.MouseEvent;
     20import java.awt.geom.AffineTransform;
    1921import java.awt.geom.Point2D;
    2022import java.awt.geom.Rectangle2D;
     
    2527import java.net.MalformedURLException;
    2628import java.net.URL;
     29import java.text.MessageFormat;
    2730import java.text.SimpleDateFormat;
    2831import java.util.ArrayList;
     
    6265import org.openstreetmap.gui.jmapviewer.OsmTileLoader;
    6366import org.openstreetmap.gui.jmapviewer.Tile;
     67import org.openstreetmap.gui.jmapviewer.TileAnchor;
    6468import org.openstreetmap.gui.jmapviewer.TileRange;
    6569import org.openstreetmap.gui.jmapviewer.TileXY;
     
    10441048     * @return  the image of the tile or null.
    10451049     */
    1046     private Image getLoadedTileImage(Tile tile) {
    1047         Image img = tile.getImage();
     1050    private BufferedImage getLoadedTileImage(Tile tile) {
     1051        BufferedImage img = tile.getImage();
    10481052        if (!imageLoaded(img))
    10491053            return null;
     
    10511055    }
    10521056
    1053     // 'source' is the pixel coordinates for the area that the img is capable of filling in.
    1054     // However, we probably only want a portion of it.
    1055     //
    1056     // 'border' is the screen cordinates that need to be drawn. We must not draw outside of it.
    1057     private void drawImageInside(Graphics g, Image sourceImg, Rectangle2D source, Rectangle2D border) {
    1058         Rectangle2D target = source;
    1059 
    1060         // If a border is specified, only draw the intersection if what we have combined with what we are supposed to draw.
    1061         if (border != null) {
    1062             target = source.createIntersection(border);
    1063             if (Main.isDebugEnabled()) {
    1064                 Main.debug("source: " + source + "\nborder: " + border + "\nintersection: " + target);
    1065             }
    1066         }
    1067 
    1068         // All of the rectangles are in screen coordinates. We need to how these correlate to the sourceImg pixels.
    1069         // We could avoid doing this by scaling the image up to the 'source' size, but this should be cheaper.
    1070         //
    1071         // In some projections, x any y are scaled differently enough to
    1072         // cause a pixel or two of fudge.  Calculate them separately.
    1073         double imageYScaling = sourceImg.getHeight(this) / source.getHeight();
    1074         double imageXScaling = sourceImg.getWidth(this) / source.getWidth();
    1075 
    1076         // How many pixels into the 'source' rectangle are we drawing?
    1077         double screenXoffset = target.getX() - source.getX();
    1078         double screenYoffset = target.getY() - source.getY();
    1079         // And how many pixels into the image itself does that correlate to?
    1080         int imgXoffset = (int) (screenXoffset * imageXScaling + 0.5);
    1081         int imgYoffset = (int) (screenYoffset * imageYScaling + 0.5);
    1082         // Now calculate the other corner of the image that we need
    1083         // by scaling the 'target' rectangle's dimensions.
    1084         int imgXend = imgXoffset + (int) (target.getWidth() * imageXScaling + 0.5);
    1085         int imgYend = imgYoffset + (int) (target.getHeight() * imageYScaling + 0.5);
    1086 
    1087         if (Main.isDebugEnabled()) {
    1088             Main.debug("drawing image into target rect: " + target);
    1089         }
    1090         g.drawImage(sourceImg,
    1091                 (int) target.getX(), (int) target.getY(),
    1092                 (int) target.getMaxX(), (int) target.getMaxY(),
    1093                 imgXoffset, imgYoffset,
    1094                 imgXend, imgYend,
    1095                 this);
    1096     }
    1097 
    1098     private List<Tile> paintTileImages(Graphics g, TileSet ts) {
     1057    /**
     1058     * Draw a tile image on screen.
     1059     * @param g the Graphics2D
     1060     * @param toDrawImg tile image
     1061     * @param anchorImage tile anchor in image coordinates
     1062     * @param anchorScreen tile anchor in screen coordinates
     1063     * @param clip clipping region in screen coordinates (can be null)
     1064     */
     1065    private void drawImageInside(Graphics2D g, BufferedImage toDrawImg, TileAnchor anchorImage, TileAnchor anchorScreen, Shape clip) {
     1066        AffineTransform imageToScreen = anchorImage.convert(anchorScreen);
     1067        Point2D screen0 = imageToScreen.transform(new Point.Double(0, 0), null);
     1068        Point2D screen1 = imageToScreen.transform(new Point.Double(toDrawImg.getWidth(), toDrawImg.getHeight()), null);
     1069        Shape oldClip = null;
     1070        if (clip != null) {
     1071            oldClip = g.getClip();
     1072            g.clip(clip);
     1073        }
     1074        g.drawImage(toDrawImg, (int) Math.round(screen0.getX()), (int) Math.round(screen0.getY()),
     1075                (int) Math.round(screen1.getX() - screen0.getX()), (int) Math.round(screen1.getY() - screen0.getY()), this);
     1076        if (clip != null) {
     1077            g.setClip(oldClip);
     1078        }
     1079    }
     1080
     1081    private List<Tile> paintTileImages(Graphics2D g, TileSet ts) {
    10991082        Object paintMutex = new Object();
    11001083        List<TilePosition> missed = Collections.synchronizedList(new ArrayList<>());
    11011084        ts.visitTiles(tile -> {
    11021085            boolean miss = false;
    1103             Image img = null;
     1086            BufferedImage img = null;
     1087            TileAnchor anchorImage = null;
    11041088            if (!tile.isLoaded() || tile.hasError()) {
    11051089                miss = true;
    11061090            } else {
    1107                 img = getLoadedTileImage(tile);
     1091                synchronized (tile) {
     1092                    img = getLoadedTileImage(tile);
     1093                    anchorImage = tile.getAnchor();
     1094                }
    11081095                if (img == null) {
    11091096                    miss = true;
     
    11151102            }
    11161103            img = applyImageProcessors((BufferedImage) img);
    1117             Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile);
     1104            TileAnchor anchorScreen = coordinateConverter.getScreenAnchorForTile(tile);
    11181105            synchronized (paintMutex) {
    11191106                //cannot paint in parallel
    1120                 drawImageInside(g, img, sourceRect, null);
     1107                drawImageInside(g, img, anchorImage, anchorScreen, null);
    11211108            }
    11221109        }, missed::add);
     
    11321119    // It will not be from the zoom level that is being drawn currently.
    11331120    // If drawing the displayZoomLevel, border is null and we draw the entire tile set.
    1134     private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
     1121    private List<Tile> paintTileImages(Graphics2D g, TileSet ts, int zoom, Tile border) {
    11351122        if (zoom <= 0) return Collections.emptyList();
    1136         Rectangle2D borderRect = coordinateConverter.getRectangleForTile(border);
     1123        Shape borderClip = coordinateConverter.getScreenQuadrilateralForTile(border);
    11371124        List<Tile> missedTiles = new LinkedList<>();
    11381125        // The callers of this code *require* that we return any tiles that we do not draw in missedTiles.
     
    11411128        for (Tile tile : ts.allTilesCreate()) {
    11421129            boolean miss = false;
    1143             Image img = null;
     1130            BufferedImage img = null;
     1131            TileAnchor anchorImage = null;
    11441132            if (!tile.isLoaded() || tile.hasError()) {
    11451133                miss = true;
    11461134            } else {
    1147                 img = getLoadedTileImage(tile);
     1135               synchronized (tile) {
     1136                    img = getLoadedTileImage(tile);
     1137                    anchorImage = tile.getAnchor();
     1138                }
    11481139                if (img == null) {
    11491140                    miss = true;
     
    11581149            img = applyImageProcessors((BufferedImage) img);
    11591150
    1160             Rectangle2D sourceRect = coordinateConverter.getRectangleForTile(tile);
    1161             Rectangle2D clipRect;
     1151            Shape clip;
    11621152            if (tileSource.isInside(tile, border)) {
    1163                 clipRect = null;
     1153                clip = null;
    11641154            } else if (tileSource.isInside(border, tile)) {
    1165                 clipRect = borderRect;
     1155                clip = borderClip;
    11661156            } else {
    11671157                continue;
    11681158            }
    1169             drawImageInside(g, img, sourceRect, clipRect);
     1159            TileAnchor anchorScreen = coordinateConverter.getScreenAnchorForTile(tile);
     1160            drawImageInside(g, img, anchorImage, anchorScreen, clip);
    11701161        }
    11711162        return missedTiles;
Note: See TracChangeset for help on using the changeset viewer.