Index: src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(revision 14578)
+++ 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;
 
@@ -436,7 +437,7 @@
      * @param disabled If this should be drawn with a special disabled style.
      */
     protected void drawArea(MapViewPath path, Color color,
-            MapImage fillImage, Float extent, Path2D.Double pfClip, boolean disabled) {
+            MapImage fillImage, Float extent, MapViewPath pfClip, boolean disabled) {
         if (!isOutlineOnly && color.getAlpha() != 0) {
             Shape area = path;
             g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
@@ -477,7 +478,7 @@
      * @param mitterLimit parameter for BasicStroke
      *
      */
-    private void computeFill(Shape shape, Float extent, Path2D.Double pfClip, float mitterLimit) {
+    private void computeFill(Shape shape, Float extent, MapViewPath pfClip, float mitterLimit) {
         if (extent == null) {
             g.fill(shape);
         } else {
@@ -484,7 +485,7 @@
             Shape oldClip = g.getClip();
             Shape clip = shape;
             if (pfClip != null) {
-                clip = pfClip.createTransformedShape(mapState.getAffineTransform());
+                clip = pfClip;
             }
             g.clip(clip);
             g.setStroke(new BasicStroke(2 * extent, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, mitterLimit));
@@ -514,15 +515,13 @@
                 if (!isAreaVisible(pd.get())) {
                     continue;
                 }
-                MapViewPath p = new MapViewPath(mapState);
-                p.appendFromEastNorth(pd.get());
-                p.setWindingRule(Path2D.WIND_EVEN_ODD);
-                Path2D.Double pfClip = null;
+                MapViewPath p = shapeEastNorthToMapView(pd.get());
+                MapViewPath pfClip = null;
                 if (extent != null) {
                     if (!usePartialFill(pd.getAreaAndPerimeter(null), extent, extentThreshold)) {
                         extent = null;
                     } else if (!pd.isClosed()) {
-                        pfClip = getPFClip(pd, extent * scale);
+                        pfClip = shapeEastNorthToMapView(getPFClip(pd, extent * scale));
                     }
                 }
                 drawArea(p,
@@ -533,6 +532,32 @@
     }
 
     /**
+     * Convert shape in EastNorth coordinates to MapViewPath and remove invisible parts.
+     * For complex shapes this improves performance drastically because the methods in Graphics2D.clip() and Graphics2D.draw() are rather slow.
+     * @param shape the shape to convert
+     * @return the converted shape
+     */
+    private MapViewPath shapeEastNorthToMapView(Path2D.Double shape) {
+        MapViewPath convertedShape = null;
+        if (shape != null) {
+            convertedShape = new MapViewPath(mapState);
+            convertedShape.appendFromEastNorth(shape);
+            convertedShape.setWindingRule(Path2D.WIND_EVEN_ODD);
+
+            Rectangle2D extViewBBox = mapState.getViewClipRectangle().getInView();
+            if (!extViewBBox.contains(convertedShape.getBounds2D())) {
+                // remove invisible parts of shape
+                Path2D.Double clipped = ShapeClipper.clipShape(convertedShape, extViewBBox);
+                if (clipped != null) {
+                    convertedShape.reset();
+                    convertedShape.append(clipped, false);
+                }
+            }
+        }
+        return convertedShape;
+    }
+
+    /**
      * Draws an area defined by a way. They way does not need to be closed, but it should.
      * @param w The way.
      * @param color The color to fill the area with.
@@ -546,12 +571,12 @@
      * @since 12285
      */
     public void drawArea(IWay<?> w, Color color, MapImage fillImage, Float extent, Float extentThreshold, boolean disabled) {
-        Path2D.Double pfClip = null;
+        MapViewPath pfClip = null;
         if (extent != null) {
             if (!usePartialFill(Geometry.getAreaAndPerimeter(w.getNodes()), extent, extentThreshold)) {
                 extent = null;
             } else if (!w.isClosed()) {
-                pfClip = getPFClip(w, extent * scale);
+                pfClip = shapeEastNorthToMapView(getPFClip(w, extent * scale));
             }
         }
         drawArea(getPath(w), color, fillImage, extent, pfClip, disabled);
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,274 @@
+// 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 util 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) {
+        Path2D.Double result = new Path2D.Double();
+        double minX, minY, maxX, maxY;
+        int num = 0;
+        minX = minY = Double.POSITIVE_INFINITY;
+        maxX = maxY = Double.NEGATIVE_INFINITY;
+
+        PathIterator pit = shape.getPathIterator(null);
+        double[] points = new double[512];
+        double[] res = new double[6];
+        while (!pit.isDone()) {
+            int type = pit.currentSegment(res);
+            if (num > 0 && (type == PathIterator.SEG_CLOSE || type == PathIterator.SEG_MOVETO || pit.isDone())) {
+                // we have extracted a single segment, maybe unclosed
+                addToResult(result, points, num, minX, minY, maxX, maxY, clippingRect);
+                num = 0;
+                minX = minY = Double.POSITIVE_INFINITY;
+                maxX = maxY = Double.NEGATIVE_INFINITY;
+            }
+            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;
+            if (type == PathIterator.SEG_LINETO || type == PathIterator.SEG_MOVETO) {
+                if (num + 2 >= points.length) {
+                    points = Arrays.copyOf(points, points.length * 2);
+                }
+                points[num++] = x;
+                points[num++] = y;
+            } else if (type != PathIterator.SEG_CLOSE) {
+                    //Logging.warn("unhandled path iterator");
+            }
+            pit.next();
+        }
+        if (num > 2) {
+            // last segment was not closed
+            addToResult(result, points, num, minX, minY, maxX, maxY, clippingRect);
+        }
+        return result;
+    }
+
+
+    private static void addToResult(Path2D.Double result, double[] points, int num,
+            double minX, double minY, double maxX, double maxY, Rectangle2D clippingRect) {
+        Path2D.Double segment = null;
+        if (clippingRect.contains(minX, minY) && clippingRect.contains(maxX, maxY)) {
+            // all points are inside clipping rectangle
+            segment = pointsToPath2D(points, num);
+        } else {
+            Rectangle2D.Double bbox = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
+            segment = clipSinglePathWithSutherlandHodgman(points, num, clippingRect, bbox);
+        }
+        if (segment != null) {
+            result.append(segment, false);
+        }
+    }
+
+    /**
+     * 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();
+        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;
+        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);
+    }
+}
