Index: src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(revision 14575)
+++ src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(working copy)
@@ -87,6 +87,7 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.ShapeClipper;
 import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.bugreport.BugReport;
 
@@ -485,10 +486,19 @@
             Shape clip = shape;
             if (pfClip != null) {
                 clip = pfClip.createTransformedShape(mapState.getAffineTransform());
+            } else if (!mapState.isInsideViewClipRectangle(shape)) {
+                // clip with fast algorithm first
+                Shape clip2 = ShapeClipper.clipShape(shape, mapState.getViewClipRectangle().getInView());
+                if (clip2 != null) {
+                    clip = clip2;
+                }
             }
             g.clip(clip);
             g.setStroke(new BasicStroke(2 * extent, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, mitterLimit));
-            g.draw(shape);
+            if (pfClip == null)
+                g.draw(shape);
+            else
+                g.draw(clip);
             g.setClip(oldClip);
             g.setStroke(new BasicStroke());
         }
Index: src/org/openstreetmap/josm/gui/MapViewState.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapViewState.java	(revision 14575)
+++ src/org/openstreetmap/josm/gui/MapViewState.java	(working copy)
@@ -3,6 +3,7 @@
 
 import java.awt.Container;
 import java.awt.Point;
+import java.awt.Shape;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Area;
 import java.awt.geom.Path2D;
@@ -304,6 +305,26 @@
     }
 
     /**
+     * Gets a rectangle that is several pixel bigger than the view in EastNorth.
+     * @return The rectangle.
+     */
+    public Rectangle2D getEastNorthClipRectangle() {
+        return new Rectangle2D.Double(topLeft.east() - CLIP_BOUNDS, topLeft.north() - (viewHeight + CLIP_BOUNDS),
+                viewWidth + 2 * CLIP_BOUNDS, viewHeight + 2 * CLIP_BOUNDS);
+    }
+
+    /**
+     * Test if the area is inside the view clip rectangle (it may touch it).
+     * @param area The area, interpreted in east/north space.
+     * @return true if it is inside or touches it
+     */
+    public boolean isInsideViewClipRectangle(Shape area) {
+        Rectangle2D bounds = area.getBounds2D();
+        if (bounds.isEmpty()) return false;
+        return (getEastNorthClipRectangle().contains(bounds));
+    }
+
+    /**
      * Returns the area for the given bounds.
      * @param bounds bounds
      * @return the area for the given bounds
Index: src/org/openstreetmap/josm/tools/ShapeClipper.java
===================================================================
--- src/org/openstreetmap/josm/tools/ShapeClipper.java	(nonexistent)
+++ src/org/openstreetmap/josm/tools/ShapeClipper.java	(working copy)
@@ -0,0 +1,287 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.awt.Shape;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.util.Arrays;
+
+/**
+ * Tools to clip a shape based on the Sutherland-Hodgman algorithm.
+ * See https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
+ * @author Gerd Petermann
+ *
+ */
+public final class ShapeClipper {
+    private static final int LEFT = 0;
+    private static final int TOP = 1;
+    private static final int RIGHT = 2;
+    private static final int BOTTOM = 3;
+
+    private ShapeClipper() {
+        // Hide default constructor for utils classes
+    }
+
+    /**
+     * Clip a given (closed) shape with a given rectangle.
+     * @param shape the subject shape to clip
+     * @param clippingRect the clipping rectangle
+     * @return the intersection of the shape and the rectangle
+     * or null if they don't intersect or the shape is not closed.
+     * The intersection may contain dangling edges.
+     */
+    public static Path2D.Double clipShape(Shape shape, Rectangle2D clippingRect) {
+        double minX = Double.POSITIVE_INFINITY, minY = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY,
+                maxY = Double.NEGATIVE_INFINITY;
+        PathIterator pit = shape.getPathIterator(null);
+        double[] points = new double[512];
+        int num = 0;
+        Path2D.Double result = null;
+        double[] res = new double[6];
+        while (!pit.isDone()) {
+            int type = pit.currentSegment(res);
+            double x = res[0];
+            double y = res[1];
+            if (x < minX)
+                minX = x;
+            if (x > maxX)
+                maxX = x;
+            if (y < minY)
+                minY = y;
+            if (y > maxY)
+                maxY = y;
+            switch (type) {
+            case PathIterator.SEG_LINETO:
+            case PathIterator.SEG_MOVETO:
+                if (num + 2 >= points.length) {
+                    points = Arrays.copyOf(points, points.length * 2);
+                }
+                points[num++] = x;
+                points[num++] = y;
+                break;
+            case PathIterator.SEG_CLOSE:
+                Path2D.Double segment = null;
+                if (!clippingRect.contains(minX, minY) || !clippingRect.contains(maxX, maxY)) {
+                    Rectangle2D.Double bbox = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
+                    segment = clipSinglePathWithSutherlandHodgman(points, num, clippingRect, bbox);
+                } else
+                    segment = pointsToPath2D(points, num);
+                if (segment != null) {
+                    if (result == null)
+                        result = segment;
+                    else
+                        result.append(segment, false);
+                }
+                num = 0;
+                minX = minY = Double.POSITIVE_INFINITY;
+                maxX = maxY = Double.NEGATIVE_INFINITY;
+                break;
+            default:
+                Logging.error("Unsupported path iterator type " + type + ". This is not supported by " + ShapeClipper.class.getName() + ".");
+            }
+            pit.next();
+        }
+        if (num >= 2) {
+            // last type was not a SEG_CLOSE
+            if (points[0] == points[num - 2] && points[1] == points[num - 1]) {
+                // not closed with closePath() but first and last point are equal
+                Path2D.Double segment = null;
+                if (!clippingRect.contains(minX, minY) || !clippingRect.contains(maxX, maxY)) {
+                    Rectangle2D.Double bbox = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
+                    segment = clipSinglePathWithSutherlandHodgman(points, num, clippingRect, bbox);
+                } else
+                    segment = pointsToPath2D(points, num);
+                if (segment != null) {
+                    if (result == null)
+                        result = segment;
+                    else
+                        result.append(segment, false);
+                }
+            } else {
+                Logging.warn(ShapeClipper.class.getName() + " shape is not closed.");
+                return null;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Convert a list of points to a Path2D.Double
+     * @param points the pairs
+     * @param num the number of valid values in points
+     * @return the path or null if the path describes a point or line.
+     */
+    private static Path2D.Double pointsToPath2D(double[] points, int num) {
+        if (num < 2)
+            return null;
+        if (points[0] == points[num - 2] && points[1] == points[num - 1])
+            num -= 2;
+        if (num < 6)
+            return null;
+        Path2D.Double path = new Path2D.Double(Path2D.WIND_NON_ZERO, num / 2 + 2);
+        double lastX = points[0], lastY = points[1];
+        path.moveTo(lastX, lastY);
+        int numOut = 1;
+        for (int i = 2; i < num;) {
+            double x = points[i++], y = points[i++];
+            if (x != lastX || y != lastY) {
+                path.lineTo(x, y);
+                lastX = x;
+                lastY = y;
+                ++numOut;
+            }
+        }
+        if (numOut < 3)
+            return null;
+        path.closePath();
+        return path;
+    }
+
+    /**
+     * Clip a single path with a given rectangle using the Sutherland-Hodgman algorithm. This is much faster compared to
+     * the area.intersect method, but may create dangling edges.
+     * @param points a list of longitude+latitude pairs
+     * @param num the number of valid values in points
+     * @param clippingRect the clipping rectangle
+     * @param bbox the bounding box of the path
+     * @return the clipped path as a Path2D.Double or null if the result is empty
+     */
+    private static Path2D.Double clipSinglePathWithSutherlandHodgman(double[] points, int num, Rectangle2D clippingRect,
+            Rectangle2D.Double bbox) {
+        if (num <= 2 || !bbox.intersects(clippingRect)) {
+            return null;
+        }
+
+        int countVals = num;
+        if (points[0] == points[num - 2] && points[1] == points[num - 1]) {
+            countVals -= 2;
+        }
+        double[] outputList = points;
+        double[] input;
+
+        double leftX = clippingRect.getMinX();
+        double rightX = clippingRect.getMaxX();
+        double lowerY = clippingRect.getMinY();
+        double upperY = clippingRect.getMaxY();
+        boolean eIsIn = false, sIsIn = false;
+        for (int side = LEFT; side <= BOTTOM; side++) {
+            if (countVals < 6)
+                return null; // ignore point or line
+
+            boolean skipTestForThisSide;
+            switch (side) {
+            case LEFT:
+                skipTestForThisSide = (bbox.getMinX() >= leftX);
+                break;
+            case TOP:
+                skipTestForThisSide = (bbox.getMaxY() < upperY);
+                break;
+            case RIGHT:
+                skipTestForThisSide = (bbox.getMaxX() < rightX);
+                break;
+            default:
+                skipTestForThisSide = (bbox.getMinY() >= lowerY);
+            }
+            if (skipTestForThisSide)
+                continue;
+
+            input = outputList;
+            outputList = new double[countVals + 16];
+            double sLon = 0, sLat = 0;
+            double pLon = 0, pLat = 0; // intersection
+            int posIn = countVals - 2;
+            int posOut = 0;
+            for (int i = 0; i < countVals + 2; i += 2) {
+                if (posIn >= countVals)
+                    posIn = 0;
+                double eLon = input[posIn++];
+                double eLat = input[posIn++];
+                switch (side) {
+                case LEFT:
+                    eIsIn = (eLon >= leftX);
+                    break;
+                case TOP:
+                    eIsIn = (eLat < upperY);
+                    break;
+                case RIGHT:
+                    eIsIn = (eLon < rightX);
+                    break;
+                default:
+                    eIsIn = (eLat >= lowerY);
+                }
+                if (i > 0) {
+                    if (eIsIn != sIsIn) {
+                        // compute intersection
+                        double slope;
+                        if (eLon != sLon)
+                            slope = (eLat - sLat) / (eLon - sLon);
+                        else
+                            slope = 1;
+
+                        switch (side) {
+                        case LEFT:
+                            pLon = leftX;
+                            pLat = slope * (leftX - sLon) + sLat;
+                            break;
+                        case RIGHT:
+                            pLon = rightX;
+                            pLat = slope * (rightX - sLon) + sLat;
+                            break;
+
+                        case TOP:
+                            if (eLon != sLon)
+                                pLon = sLon + (upperY - sLat) / slope;
+                            else
+                                pLon = sLon;
+                            pLat = upperY;
+                            break;
+                        default: // BOTTOM
+                            if (eLon != sLon)
+                                pLon = sLon + (lowerY - sLat) / slope;
+                            else
+                                pLon = sLon;
+                            pLat = lowerY;
+                            break;
+
+                        }
+                    }
+                    int toAdd = 0;
+                    if (eIsIn) {
+                        if (!sIsIn) {
+                            toAdd += 2;
+                        }
+                        toAdd += 2;
+                    } else {
+                        if (sIsIn) {
+                            toAdd += 2;
+                        }
+                    }
+                    if (posOut + toAdd >= outputList.length) {
+                        // unlikely
+                        outputList = Arrays.copyOf(outputList, outputList.length * 2);
+                    }
+                    if (eIsIn) {
+                        if (!sIsIn) {
+                            outputList[posOut++] = pLon;
+                            outputList[posOut++] = pLat;
+                        }
+                        outputList[posOut++] = eLon;
+                        outputList[posOut++] = eLat;
+                    } else {
+                        if (sIsIn) {
+                            outputList[posOut++] = pLon;
+                            outputList[posOut++] = pLat;
+                        }
+                    }
+                }
+                // S = E
+                sLon = eLon;
+                sLat = eLat;
+                sIsIn = eIsIn;
+            }
+            countVals = posOut;
+        }
+        return pointsToPath2D(outputList, countVals);
+    }
+}
