Ignore:
Timestamp:
2009-10-29T22:32:51+01:00 (15 years ago)
Author:
dhansen
Message:

Tons of updates:

  • Don't insert tiles into the cache that we just created to help with calculations (tempCornerTile())
  • Introduce TileSet::tooLarge/tooSmall() to centralize where we define the tile set limits.
  • Check those new limits in a few places to avoid creating too many tile objects.
  • We now draw *something* if we exceed the tile size limits. We don't load new tiles, but we draw what we have.
  • Add some messages when the user exceeds those zoom limits to give them a hint on what to do.
  • Rewrite the image drawing code, largely:
    • This now keeps explicit track of the tiles that were drawn and those that were unable to be drawn.
    • If we can not draw a tile, we try to draw its area from another zoom level. We only draw the exact area that was missing.
    • We keep drawing the missing areas until there are none left or we run out of zoom levels.
    • This should eliminate all redundant drawing
    • This also unifies the current zoom and "otherZoom" code.
    • The end result is much faster, cleaner drawing with no flickering.
  • Draw the debug lines a bit differently, not depending on tile objects and their presence in the TileSet as much.
  • Don't pad the TileSet contents as much. This wasted bandwidth for things that were not being drawn on the screen.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java

    r18360 r18367  
    173173        }));
    174174
     175        /* FIXME
    175176        tileOptionMenu.add(new JMenuItem(new AbstractAction(
    176177                tr("Request Update")) {
    177178            public void actionPerformed(ActionEvent ae) {
    178179                if (clickedTile != null) {
    179                     //FIXME//clickedTile.requestUpdate();
     180                    clickedTile.requestUpdate();
    180181                    redraw();
    181182                }
    182183            }
    183         }));
     184        }));*/
    184185
    185186        tileOptionMenu.add(new JMenuItem(new AbstractAction(
     
    204205                    public void actionPerformed(ActionEvent ae) {
    205206                        decreaseZoomLevel();
    206                         Main.map.repaint();
     207                        redraw();
    207208                    }
    208209                }));
     
    337338    }
    338339
     340    /*
     341     * We use these for quick, hackish calculations.  They
     342     * are temporary only and intentionally not inserted
     343     * into the tileCache.
     344     */
     345    synchronized Tile tempCornerTile(Tile t) {
     346        int x = t.getXtile() + 1;
     347        int y = t.getYtile() + 1;
     348        int zoom = t.getZoom();
     349        Tile tile = getTile(x, y, zoom);
     350        if (tile != null)
     351            return tile;
     352        return new Tile(tileSource, x, y, zoom);
     353    }
    339354    synchronized Tile getOrCreateTile(int x, int y, int zoom) {
    340355        Tile tile = getTile(x, y, zoom);
     
    386401        // if there is more than 18 tiles on screen in any direction, do not
    387402        // load all tiles!
    388         if (ts.tilesSpanned() > (18*18)) {
     403        if (ts.tooLarge()) {
    389404            System.out.println("Not downloading all tiles because there is more than 18 tiles on an axis!");
    390405            return;
     
    401416        boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
    402417        needRedraw = true;
     418        if (debug)
     419            out("imageUpdate() done: " + done + " calling repaint");
    403420        Main.map.repaint(done ? 0 : 100);
    404421        return !done;
     
    422439    }
    423440
    424     double getImageScaling(Image img, Point p0, Point p1) {
     441    double getImageScaling(Image img, Rectangle r) {
    425442        int realWidth = -1;
    426443        int realHeight = -1;
    427            if (img != null) {
     444        if (img != null) {
    428445            realWidth = img.getHeight(this);
    429                realWidth = img.getWidth(this);
     446            realWidth = img.getWidth(this);
    430447        }
    431448        if (realWidth == -1 || realHeight == -1) {
     
    437454             */
    438455            if (lastScaledImage != null) {
    439                 return getImageScaling(lastScaledImage, p0, p1);
     456                return getImageScaling(lastScaledImage, r);
    440457            }
    441458            realWidth = 256;
     
    449466         * overflows.
    450467         */
    451         double drawWidth = p1.x - p0.x;
    452         double drawHeight = p1.x - p0.x;
     468        double drawWidth = r.width;
     469        double drawHeight = r.height;
    453470        // stem.out.println("drawWidth: " + drawWidth + " drawHeight: " +
    454471        // drawHeight);
     
    472489        LatLon botRight = tileLatLon(botRightTile);
    473490
    474         if (!autoZoomEnabled())
    475             return 0;
    476         if (!SlippyMapPreferences.getAutoloadTiles())
    477             return 0;
    478491
    479492        /*
     
    489502        int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5};
    490503        int painted = 0;;
     504        debug = true;
    491505        for (int zoomOff : otherZooms) {
    492506            int zoom = currentZoomLevel + zoomOff;
     
    496510            }
    497511            TileSet ts = new TileSet(topLeft, botRight, zoom);
    498             int zoom_painted = this.paintTileImages(g, ts, zoom);
     512            int zoom_painted = 0;
     513            this.paintTileImages(g, ts, zoom, null);
    499514            if (debug && zoom_painted > 0)
    500515                out("painted " + zoom_painted + "/"+ ts.size() +
     
    507522            }
    508523        }
     524        debug = false;
    509525        return painted;
    510526    }
     527    Rectangle tileToRect(Tile t1)
     528    {
     529        /*
     530         * We need to get a box in which to draw, so advance by one tile in
     531         * each direction to find the other corner of the box.
     532         * Note: this somewhat pollutes the tile cache
     533         */
     534        Tile t2 = tempCornerTile(t1);
     535        Rectangle rect = new Rectangle(pixelPos(t1));
     536        rect.add(pixelPos(t2));
     537        return rect;
     538    }
     539
     540    // 'source' is the pixel coordinates for the area that
     541    // the img is capable of filling in.  However, we probably
     542    // only want a portion of it.
     543    //
     544    // 'border' is the screen cordinates that need to be drawn.
     545    //  We must not draw outside of it.
     546    void drawImageInside(Graphics g, Image sourceImg, Rectangle source, Rectangle border)
     547    {
     548        Rectangle target = source;
     549
     550        // If a border is specified, only draw the intersection
     551        // if what we have combined with what we are supposed
     552        // to draw.
     553        if (border != null) {
     554            target = source.intersection(border);
     555            if (debug)
     556                out("source: " + source + "\nborder: " + border + "\nintersection: " + target);
     557        }
     558
     559        // All of the rectangles are in screen coordinates.  We need
     560        // to how these correlate to the sourceImg pixels.  We could
     561        // avoid doing this by scaling the image up to the 'source' size,
     562        // but this should be cheaper.
     563        //
     564        // In some projections, x any y are scaled differently enough to
     565        // cause a pixel or two of fudge.  Calculate them separately.
     566        double imageYScaling = sourceImg.getHeight(this) / source.getHeight();
     567        double imageXScaling = sourceImg.getWidth(this) / source.getWidth();
     568
     569        // How many pixels into the 'source' rectangle are we drawing?
     570        int screen_x_offset = target.x - source.x;
     571        int screen_y_offset = target.y - source.y;
     572        // And how many pixels into the image itself does that
     573        // correlate to?
     574        int img_x_offset = (int)(screen_x_offset * imageXScaling);
     575        int img_y_offset = (int)(screen_y_offset * imageYScaling);
     576        // Now calculate the other corner of the image that we need
     577        // by scaling the 'target' rectangle's dimensions.
     578        int img_x_end   = img_x_offset + (int)(target.getWidth() * imageXScaling);
     579        int img_y_end   = img_y_offset + (int)(target.getHeight() * imageYScaling);
     580
     581        if (debug) {
     582            out("drawing image into target rect: " + target);
     583        }
     584        g.drawImage(sourceImg,
     585                        target.x, target.y,
     586                        target.x + target.width, target.y + target.height,
     587                        img_x_offset, img_y_offset,
     588                        img_x_end, img_y_end,
     589                        this);
     590        float fadeBackground = SlippyMapPreferences.getFadeBackground();
     591        if (fadeBackground != 0f) {
     592            // dimm by painting opaque rect...
     593            g.setColor(new Color(1f, 1f, 1f, fadeBackground));
     594            g.fillRect(target.x, target.y,
     595                       target.width, target.height);
     596        }
     597    }
     598    Double lastImageScale = null;
    511599    // This function is called for several zoom levels, not just
    512600    // the current one.  It should not trigger any tiles to be
    513601    // downloaded.  It should also avoid polluting the tile cache
    514602    // with any tiles since these tiles are not mandatory.
    515     Double lastImageScale = null;
    516     int paintTileImages(Graphics g, TileSet ts, int zoom) {
    517         int paintedTiles = 0;
     603    //
     604    // The "border" tile tells us the boundaries of where we may
     605    // draw.  It will not be from the zoom level that is being
     606    // drawn currently.  If drawing the currentZoomLevel,
     607    // border is null and we draw the entire tile set.
     608    List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
     609        Rectangle borderRect = null;
     610        if (border != null)
     611            borderRect = tileToRect(border);
     612        List<Tile> missedTiles = new LinkedList<Tile>();
    518613        boolean imageScaleRecorded = false;
    519614        for (Tile tile : ts.allTiles()) {
    520             /*
    521              * We need to get a box in which to draw, so advance by one tile in
    522              * each direction to find the other corner of the box.
    523              * Note: this somewhat pollutes the tile cache
    524              */
    525             Tile t2 = getOrCreateTile(tile.getXtile() + 1, tile.getYtile() + 1, zoom);
    526615            Image img = getLoadedTileImage(tile);
    527             Point p = pixelPos(tile);
    528             Point p2 = pixelPos(t2);
    529             if (currentZoomLevel == zoom && img == null) {
    530                 int painted = paintFromOtherZooms(g, tile, t2);
    531                 if (painted > 0 && debug)
    532                     out("painted a total of " + painted);
     616            if (img == null) {
     617                if (debug)
     618                    out("missed tile: " + tile);
     619                missedTiles.add(tile);
    533620                continue;
    534621            }
    535             g.drawImage(img, p.x, p.y, p2.x - p.x, p2.y - p.y, this);
    536             paintedTiles++;
    537             if (!imageScaleRecorded && zoom == currentZoomLevel) {
    538                 lastImageScale = new Double(getImageScaling(img, p, p2));
     622            Rectangle sourceRect = tileToRect(tile);
     623            if (borderRect != null && !sourceRect.intersects(borderRect))
     624                continue;
     625            drawImageInside(g, img, sourceRect, borderRect);
     626            if (autoZoomEnabled() &&
     627                !imageScaleRecorded && zoom == currentZoomLevel) {
     628                lastImageScale = new Double(getImageScaling(img, sourceRect));
    539629                imageScaleRecorded = true;
    540630            }
    541             float fadeBackground = SlippyMapPreferences.getFadeBackground();
    542             if (fadeBackground != 0f) {
    543                 // dimm by painting opaque rect...
    544                 g.setColor(new Color(1f, 1f, 1f, fadeBackground));
    545                 g.fillRect(p.x, p.y, p2.x - p.x, p2.y - p.y);
    546             }
    547631        }// end of for
    548         return paintedTiles;
     632        return missedTiles;
    549633    }
    550634
     
    578662            texty += 1 + fontHeight;
    579663        }
    580         /*
    581         We already do repaint when the images load
    582         we don't need to poll like this
    583         */
    584         if (!tile.isLoaded())
    585             redraw();
    586 
     664
     665        int xCursor = -1;
     666        int yCursor = -1;
    587667        if (SlippyMapPreferences.getDrawDebug()) {
    588             if (ts.leftTile(t)) {
     668            if (yCursor < t.getYtile()) {
    589669                if (t.getYtile() % 32 == 31) {
    590670                    g.fillRect(0, p.y - 1, mv.getWidth(), 3);
     
    592672                    g.drawLine(0, p.y, mv.getWidth(), p.y);
    593673                }
    594             }
    595         }// /end of if draw debug
    596     }
    597 
     674                yCursor = t.getYtile();
     675            }
     676            // This draws the vertical lines for the entire
     677            // column. Only draw them for the top tile in
     678            // the column.
     679            if (xCursor < t.getXtile()) {
     680                if (SlippyMapPreferences.getDrawDebug()) {
     681                    if (t.getXtile() % 32 == 0) {
     682                        // level 7 tile boundary
     683                        g.fillRect(p.x - 1, 0, 3, mv.getHeight());
     684                    } else {
     685                        g.drawLine(p.x, 0, p.x, mv.getHeight());
     686                    }
     687                }
     688                xCursor = t.getXtile();
     689            }
     690        }
     691    }
     692
     693    public Point pixelPos(LatLon ll) {
     694        return Main.map.mapView.getPoint(ll);
     695    }
    598696    public Point pixelPos(Tile t) {
    599             double lon = tileXToLon(t.getXtile(), t.getZoom());
    600             LatLon tmpLL = new LatLon(tileYToLat(t.getYtile(), t.getZoom()), lon);
    601             MapView mv = Main.map.mapView;
    602             return mv.getPoint(tmpLL);
    603         }
     697        double lon = tileXToLon(t.getXtile(), t.getZoom());
     698        LatLon tmpLL = new LatLon(tileYToLat(t.getYtile(), t.getZoom()), lon);
     699        return pixelPos(tmpLL);
     700    }
    604701    private class TileSet {
    605702        int z12x0, z12x1, z12y0, z12y1;
     
    609706        TileSet(LatLon topLeft, LatLon botRight, int zoom) {
    610707            this.zoom = zoom;
    611             z12x0 = lonToTileX(topLeft.lon(), zoom) - 1;
    612             z12y0 = latToTileY(topLeft.lat(),  zoom) - 1;
    613             z12x1 = lonToTileX(botRight.lon(), zoom) + 1;
    614             z12y1 = latToTileY(botRight.lat(), zoom) + 1;
     708            z12x0 = lonToTileX(topLeft.lon(),  zoom);
     709            z12y0 = latToTileY(topLeft.lat(),  zoom);
     710            z12x1 = lonToTileX(botRight.lon(), zoom);
     711            z12y1 = latToTileY(botRight.lat(), zoom);
    615712            if (z12x0 > z12x1) {
    616713                int tmp = z12x0;
     
    628725            if (z12x1 > tileMax) z12x1 = tileMax;
    629726            if (z12y1 > tileMax) z12y1 = tileMax;
     727        }
     728        boolean tooSmall() {
     729            return this.tilesSpanned() < 2.1;
     730        }
     731        boolean tooLarge() {
     732            return this.tilesSpanned() > 10;
    630733        }
    631734        double tilesSpanned() {
     
    730833
    731834        if (autoZoomEnabled()) {
    732             if (zoomDecreaseAllowed() && (ts.tilesSpanned() > 18)) {
     835            if (zoomDecreaseAllowed() && ts.tooLarge()) {
    733836                if (debug)
    734837                    out("too many tiles, decreasing zoom from " + currentZoomLevel);
     
    737840                return;
    738841            }
    739             if (zoomIncreaseAllowed() && (ts.tilesSpanned() < 1.0)) {
     842            if (zoomIncreaseAllowed() && ts.tooSmall()) {
    740843                if (debug)
    741                     out("doesn't even cover one tile (" + ts.tilesSpanned()
     844                    out("too zoomed in, (" + ts.tilesSpanned()
    742845                        + "), increasing zoom from " + currentZoomLevel);
    743846                if (increaseZoomLevel())
     
    748851
    749852        // Too many tiles... refuse to draw
    750         if (ts.tilesSpanned() > 18)
    751             return;
    752 
    753         ts.loadAllTiles(false);
     853        if (!ts.tooLarge()) {
     854            ts.loadAllTiles(false);
     855        }
    754856
    755857        int fontHeight = g.getFontMetrics().getHeight();
     
    757859        g.setColor(Color.DARK_GRAY);
    758860
    759         this.paintTileImages(g, ts, currentZoomLevel);
     861        List<Tile> missedTiles = this.paintTileImages(g, ts, currentZoomLevel, null);
     862        int otherZooms[] = { -1, 1, -2, 2, -3, -4, -5};
     863        for (int zoomOffset : otherZooms) {
     864            if (!autoZoomEnabled())
     865                break;
     866            if (!SlippyMapPreferences.getAutoloadTiles())
     867                break;
     868            int newzoom = currentZoomLevel + zoomOffset;
     869            if (missedTiles.size() <= 0)
     870                break;
     871            List<Tile> newlyMissedTiles = new LinkedList<Tile>();
     872            for (Tile missed : missedTiles) {
     873                Tile t2 = tempCornerTile(missed);
     874                LatLon topLeft2  = tileLatLon(missed);
     875                LatLon botRight2 = tileLatLon(t2);
     876                TileSet ts2 = new TileSet(topLeft2, botRight2, newzoom);
     877                newlyMissedTiles.addAll(this.paintTileImages(g, ts2, newzoom, missed));
     878            }
     879            missedTiles = newlyMissedTiles;
     880        }
     881        if (debug && missedTiles.size() > 0) {
     882            out("still missed "+missedTiles.size()+" in the end");
     883        }
    760884        g.setColor(Color.red);
    761885
     
    763887        // its tiles
    764888        for (Tile t : ts.allTiles()) {
    765             // This draws the vertical lines for the entire
    766             // column. Only draw them for the top tile in
    767             // the column.
    768             if (ts.topTile(t)) {
    769                 Point p = pixelPos(t);
    770                 if (SlippyMapPreferences.getDrawDebug()) {
    771                     if (t.getXtile() % 32 == 0) {
    772                         // level 7 tile boundary
    773                         g.fillRect(p.x - 1, 0, 3, mv.getHeight());
    774                     } else {
    775                         g.drawLine(p.x, 0, p.x, mv.getHeight());
    776                     }
    777                 }
    778             }
    779889            this.paintTileText(ts, t, g, mv, currentZoomLevel, t);
    780890        }
     
    782892        oldg.drawImage(bufferImage, 0, 0, null);
    783893
    784         if (lastImageScale != null) {
     894        if (autoZoomEnabled() && lastImageScale != null) {
    785895            // If each source image pixel is being stretched into > 3
    786896            // drawn pixels, zoom in... getting too pixelated
    787897            if (lastImageScale > 3 && zoomIncreaseAllowed()) {
    788                 if (autoZoomEnabled()) {
    789                     if (debug)
    790                         out("autozoom increase: scale: " + lastImageScale);
    791                     increaseZoomLevel();
    792                     this.paint(oldg, mv);
    793                 }
     898                if (debug)
     899                    out("autozoom increase: scale: " + lastImageScale);
     900                increaseZoomLevel();
     901                this.paint(oldg, mv);
    794902            // If each source image pixel is being squished into > 0.32
    795903            // of a drawn pixels, zoom out.
    796904            } else if ((lastImageScale < 0.45) && (lastImageScale > 0) && zoomDecreaseAllowed()) {
    797                 if (autoZoomEnabled()) {
    798                     if (debug)
    799                         out("autozoom decrease: scale: " + lastImageScale);
    800                     decreaseZoomLevel();
    801                     this.paint(oldg, mv);
    802                 }
    803             }
    804         }
    805         g.setColor(Color.black);
    806         g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
     905                if (debug)
     906                    out("autozoom decrease: scale: " + lastImageScale);
     907                decreaseZoomLevel();
     908                this.paint(oldg, mv);
     909            }
     910        }
     911        //g.drawString("currentZoomLevel=" + currentZoomLevel, 120, 120);
     912        oldg.setColor(Color.black);
     913        if (ts.tooLarge()) {
     914            oldg.drawString("zoom in to load more tiles", 120, 120);
     915        } else if (ts.tooSmall()) {
     916            oldg.drawString("increase zoom level to see more detail", 120, 120);
     917        }
    807918    }// end of paint method
    808919
     
    821932        TileSet ts = new TileSet(topLeft, botRight, z);
    822933
    823         ts.loadAllTiles(false); // make sure there are tile objects for all tiles
     934        if (!ts.tooLarge())
     935            ts.loadAllTiles(false); // make sure there are tile objects for all tiles
    824936        Tile clickedTile = null;
    825937        Point p1 = null, p2 = null;
    826938        for (Tile t1 : ts.allTiles()) {
    827             Tile t2 = getOrCreateTile(t1.getXtile()+1, t1.getYtile()+1, z);
     939            Tile t2 = tempCornerTile(t1);
    828940            Rectangle r = new Rectangle(pixelPos(t1));
    829941            r.add(pixelPos(t2));
Note: See TracChangeset for help on using the changeset viewer.