Index: /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java	(revision 4626)
+++ /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/MapPainter.java	(revision 4627)
@@ -19,4 +19,6 @@
 import java.awt.geom.AffineTransform;
 import java.awt.geom.GeneralPath;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
@@ -29,4 +31,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -41,9 +44,9 @@
 import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle;
-import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
-import org.openstreetmap.josm.gui.mappaint.TextElement;
 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.HorizontalTextAlignment;
 import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.VerticalTextAlignment;
+import org.openstreetmap.josm.gui.mappaint.NodeElemStyle;
 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle.Symbol;
+import org.openstreetmap.josm.gui.mappaint.TextElement;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Pair;
@@ -783,33 +786,41 @@
     }
 
-    private Polygon getPolygon(Way w) {
-        Polygon polygon = new Polygon();
-
-        for (Node n : w.getNodes()) {
-            Point p = nc.getPoint(n);
-            polygon.addPoint(p.x,p.y);
-        }
-        return polygon;
+    private Path2D.Double getPath(Way w) {
+        Path2D.Double path = new Path2D.Double();
+        boolean initial = true;
+        for (Node n : w.getNodes())
+        {
+            Point2D p = n.getEastNorth();
+            if (initial) {
+                path.moveTo(p.getX(), p.getY());
+                initial = false;
+            } else {
+                path.lineTo(p.getX(), p.getY());
+            }
+        }
+        return path;
     }
 
     public void drawArea(Way w, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
-        Polygon polygon = getPolygon(w);
-        drawArea(w, polygon, color, fillImage, fillImageAlpha, text);
-    }
-
-    protected void drawArea(OsmPrimitive osm, Polygon polygon, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
-
+        drawArea(w, getPath(w), color, fillImage, fillImageAlpha, text);
+    }
+
+    protected void drawArea(OsmPrimitive osm, Path2D.Double path, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
+
+        Shape area = path.createTransformedShape(nc.getAffineTransform());
+        
         if (!isOutlineOnly) {
             if (fillImage == null) {
                 g.setColor(color);
-                g.fillPolygon(polygon);
+                g.fill(area);
             } else {
                 TexturePaint texture = new TexturePaint(fillImage,
-                        new Rectangle(polygon.xpoints[0], polygon.ypoints[0], fillImage.getWidth(), fillImage.getHeight()));
+//                        new Rectangle(polygon.xpoints[0], polygon.ypoints[0], fillImage.getWidth(), fillImage.getHeight()));
+                      new Rectangle(0, 0, fillImage.getWidth(), fillImage.getHeight()));
                 g.setPaint(texture);
                 if (fillImageAlpha != 1f) {
                     g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fillImageAlpha));
                 }
-                g.fill(polygon);
+                g.fill(area);
                 g.setPaintMode();
             }
@@ -824,5 +835,5 @@
             if (name == null) return;
 
-            Rectangle pb = polygon.getBounds();
+            Rectangle pb = area.getBounds();
             FontMetrics fontMetrics = g.getFontMetrics(orderFont); // if slow, use cache
             Rectangle2D nb = fontMetrics.getStringBounds(name, g); // if slow, approximate by strlen()*maxcharbounds(font)
@@ -847,5 +858,5 @@
 
             if ((pb.width >= nb.getWidth() && pb.height >= nb.getHeight()) && // quick check
-                    polygon.contains(centeredNBounds) // slow but nice
+                  area.contains(centeredNBounds) // slow but nice
             ) {
                 g.setColor(text.color);
@@ -862,8 +873,8 @@
     public void drawArea(Relation r, Color color, BufferedImage fillImage, float fillImageAlpha, TextElement text) {
         Multipolygon multipolygon = MultipolygonCache.getInstance().get(nc, r);
-        if(!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
+        if (!r.isDisabled() && !multipolygon.getOuterWays().isEmpty()) {
             for (PolyData pd : multipolygon.getCombinedPolygons()) {
-                Polygon p = pd.get();
-                if(!isPolygonVisible(p)) {
+                Path2D.Double p = pd.get();
+                if (!isAreaVisible(p)) {
                     continue;
                 }
@@ -875,11 +886,13 @@
     }
 
-    private boolean isPolygonVisible(Polygon polygon) {
-        Rectangle bounds = polygon.getBounds();
-        if (bounds.width == 0 && bounds.height == 0) return false;
-        if (bounds.x > nc.getWidth()) return false;
-        if (bounds.y > nc.getHeight()) return false;
-        if (bounds.x + bounds.width < 0) return false;
-        if (bounds.y + bounds.height < 0) return false;
+    private boolean isAreaVisible(Path2D.Double area) {
+        Rectangle2D bounds = area.getBounds2D();
+        if (bounds.isEmpty()) return false;
+        Point2D p = nc.getPoint2D(new EastNorth(bounds.getX(), bounds.getY()));
+        if (p.getX() > nc.getWidth()) return false;
+        if (p.getY() < 0) return false;
+        p = nc.getPoint2D(new EastNorth(bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()));
+        if (p.getX() < 0) return false;
+        if (p.getY() > nc.getHeight()) return false;
         return true;
     }
Index: /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/Multipolygon.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/Multipolygon.java	(revision 4626)
+++ /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/Multipolygon.java	(revision 4627)
@@ -2,7 +2,9 @@
 package org.openstreetmap.josm.data.osm.visitor.paint.relations;
 
-import java.awt.Point;
-import java.awt.Polygon;
-import java.awt.Rectangle;
+import java.awt.geom.Path2D;
+import java.awt.geom.Path2D.Double;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -17,5 +19,4 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData.Intersection;
-import org.openstreetmap.josm.gui.NavigatableComponent;
 
 public class Multipolygon {
@@ -170,68 +171,67 @@
         public enum Intersection {INSIDE, OUTSIDE, CROSSING}
 
-        public Polygon poly = new Polygon();
+        public final Path2D.Double poly;
         public final boolean selected;
-        private Point lastP;
-        private Rectangle bounds;
-
-        public PolyData(NavigatableComponent nc, JoinedWay joinedWay) {
-            this(nc, joinedWay.getNodes(), joinedWay.isSelected());
-        }
-
-        public PolyData(NavigatableComponent nc, List<Node> nodes, boolean selected) {
+        private Rectangle2D bounds;
+
+        public PolyData(JoinedWay joinedWay) {
+            this(joinedWay.getNodes(), joinedWay.isSelected());
+        }
+
+        public PolyData(List<Node> nodes, boolean selected) {
             this.selected = selected;
-            Point p = null;
+            boolean initial = true;
+            this.poly = new Path2D.Double();
+            this.poly.setWindingRule(Path2D.WIND_EVEN_ODD);
             for (Node n : nodes)
             {
-                p = nc.getPoint(n);
-                poly.addPoint(p.x,p.y);
-            }
-            if (!nodes.get(0).equals(nodes.get(nodes.size() - 1))) {
-                p = nc.getPoint(nodes.get(0));
-                poly.addPoint(p.x, p.y);
-            }
-            lastP = p;
+                Point2D p = n.getEastNorth();
+                if (initial) {
+                    poly.moveTo(p.getX(), p.getY());
+                    initial = false;
+                } else {
+                    poly.lineTo(p.getX(), p.getY());
+                }
+            }
+            poly.closePath();
         }
 
         public PolyData(PolyData copy) {
-            poly = new Polygon(copy.poly.xpoints, copy.poly.ypoints, copy.poly.npoints);
             this.selected = copy.selected;
-            lastP = copy.lastP;
-        }
-
-        public Intersection contains(Polygon p) {
-            int contains = p.npoints;
-            for(int i = 0; i < p.npoints; ++i)
-            {
-                if(poly.contains(p.xpoints[i],p.ypoints[i])) {
-                    --contains;
-                }
-            }
-            if(contains == 0) return Intersection.INSIDE;
-            if(contains == p.npoints) return Intersection.OUTSIDE;
+            this.poly = (Double) copy.poly.clone();
+        }
+        
+        public Intersection contains(Path2D.Double p) {
+            int contains = 0;
+            int total = 0;
+            double[] coords = new double[6];
+            for (PathIterator it = p.getPathIterator(null); !it.isDone(); it.next()) {
+                switch (it.currentSegment(coords)) {
+                    case PathIterator.SEG_MOVETO:
+                    case PathIterator.SEG_LINETO:
+                        if (poly.contains(coords[0], coords[1])) {
+                            contains++;
+                        }
+                        total++;
+                }
+            }
+            if (contains == total) return Intersection.INSIDE;
+            if (contains == 0) return Intersection.OUTSIDE;
             return Intersection.CROSSING;
         }
 
-        public void addInner(Polygon p) {
-            for(int i = 0; i < p.npoints; ++i) {
-                poly.addPoint(p.xpoints[i],p.ypoints[i]);
-            }
-            poly.addPoint(lastP.x, lastP.y);
-        }
-
-        public Polygon get() {
+        public void addInner(Path2D.Double inner) {
+            poly.append(inner.getPathIterator(null), false);
+        }
+
+        public Path2D.Double get() {
             return poly;
         }
 
-        public Rectangle getBounds() {
+        public Rectangle2D getBounds() {
             if (bounds == null) {
-                bounds = poly.getBounds();
+                bounds = poly.getBounds2D();
             }
             return bounds;
-        }
-
-        @Override
-        public String toString() {
-            return "Points: " + poly.npoints + " Selected: " + selected;
         }
     }
@@ -243,9 +243,9 @@
     private final List<PolyData> combinedPolygons = new ArrayList<PolyData>();
 
-    public Multipolygon(NavigatableComponent nc, Relation r) {
-        load(r, nc);
-    }
-
-    private void load(Relation r, NavigatableComponent nc) {
+    public Multipolygon(Relation r) {
+        load(r);
+    }
+
+    private void load(Relation r) {
         MultipolygonRoleMatcher matcher = getMultipolygonRoleMatcher();
 
@@ -253,14 +253,14 @@
         for (RelationMember m : r.getMembers()) {
             if (m.getMember().isDrawable()) {
-                if(m.isWay()) {
+                if (m.isWay()) {
                     Way w = m.getWay();
 
-                    if(w.getNodesCount() < 2) {
+                    if (w.getNodesCount() < 2) {
                         continue;
                     }
 
-                    if(matcher.isInnerRole(m.getRole())) {
+                    if (matcher.isInnerRole(m.getRole())) {
                         innerWays.add(w);
-                    } else if(matcher.isOuterRole(m.getRole())) {
+                    } else if (matcher.isOuterRole(m.getRole())) {
                         outerWays.add(w);
                     } else if (!m.hasRole()) {
@@ -271,6 +271,6 @@
         }
 
-        createPolygons(nc, innerWays, innerPolygons);
-        createPolygons(nc, outerWays, outerPolygons);
+        createPolygons(innerWays, innerPolygons);
+        createPolygons(outerWays, outerPolygons);
         if (!outerPolygons.isEmpty()) {
             addInnerToOuters();
@@ -278,9 +278,9 @@
     }
 
-    private void createPolygons(NavigatableComponent nc, List<Way> ways, List<PolyData> result) {
+    private void createPolygons(List<Way> ways, List<PolyData> result) {
         List<Way> waysToJoin = new ArrayList<Way>();
         for (Way way: ways) {
             if (way.isClosed()) {
-                result.add(new PolyData(nc, way.getNodes(), way.isSelected()));
+                result.add(new PolyData(way.getNodes(), way.isSelected()));
             } else {
                 waysToJoin.add(way);
@@ -289,5 +289,5 @@
 
         for (JoinedWay jw: joinWays(waysToJoin)) {
-            result.add(new PolyData(nc, jw));
+            result.add(new PolyData(jw));
         }
     }
@@ -390,34 +390,33 @@
 
     public PolyData findOuterPolygon(PolyData inner, List<PolyData> outerPolygons) {
+
+        // First try to test only bbox, use precise testing only if we don't get unique result
+        Rectangle2D innerBox = inner.getBounds();
+        PolyData insidePolygon = null;
+        PolyData intersectingPolygon = null;
+        int insideCount = 0;
+        int intersectingCount = 0;
+
+        for (PolyData outer: outerPolygons) {
+            if (outer.getBounds().contains(innerBox)) {
+                insidePolygon = outer;
+                insideCount++;
+            } else if (outer.getBounds().intersects(innerBox)) {
+                intersectingPolygon = outer;
+                intersectingCount++;
+            }
+        }
+        
+        if (insideCount == 1)
+            return insidePolygon;
+        else if (intersectingCount == 1)
+            return intersectingPolygon;
+
         PolyData result = null;
-
-        {// First try to test only bbox, use precise testing only if we don't get unique result
-            Rectangle innerBox = inner.getBounds();
-            PolyData insidePolygon = null;
-            PolyData intersectingPolygon = null;
-            int insideCount = 0;
-            int intersectingCount = 0;
-
-            for (PolyData outer: outerPolygons) {
-                if (outer.getBounds().contains(innerBox)) {
-                    insidePolygon = outer;
-                    insideCount++;
-                } else if (outer.getBounds().intersects(innerBox)) {
-                    intersectingPolygon = outer;
-                    intersectingCount++;
-                }
-            }
-
-            if (insideCount == 1)
-                return insidePolygon;
-            else if (intersectingCount == 1)
-                return intersectingPolygon;
-        }
-
         for (PolyData combined : outerPolygons) {
             Intersection c = combined.contains(inner.poly);
-            if(c != Intersection.OUTSIDE)
+            if (c != Intersection.OUTSIDE)
             {
-                if(result == null || result.contains(combined.poly) != Intersection.INSIDE) {
+                if (result == null || result.contains(combined.poly) != Intersection.INSIDE) {
                     result = combined;
                 }
@@ -444,5 +443,5 @@
             for (PolyData pdInner: innerPolygons) {
                 PolyData o = findOuterPolygon(pdInner, combinedPolygons);
-                if(o == null) {
+                if (o == null) {
                     o = outerPolygons.get(0);
                 }
Index: /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java	(revision 4626)
+++ /trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java	(revision 4627)
@@ -26,5 +26,4 @@
 import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
 import org.openstreetmap.josm.gui.NavigatableComponent;
-import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
@@ -34,5 +33,5 @@
  * 
  */
-public class MultipolygonCache implements DataSetListener, LayerChangeListener, ZoomChangeListener, ProjectionChangeListener {
+public class MultipolygonCache implements DataSetListener, LayerChangeListener, ProjectionChangeListener {
 
     private static final MultipolygonCache instance = new MultipolygonCache(); 
@@ -41,5 +40,5 @@
     
     private MultipolygonCache() {
-        this.cache = new HashMap<NavigatableComponent, Map<DataSet, Map<Relation,Multipolygon>>>();
+        this.cache = new HashMap<NavigatableComponent, Map<DataSet, Map<Relation, Multipolygon>>>();
         Main.addProjectionChangeListener(this);
     }
@@ -66,5 +65,5 @@
             multipolygon = map2.get(r);
             if (multipolygon == null || forceRefresh) {
-                map2.put(r, multipolygon = new Multipolygon(nc, r));
+                map2.put(r, multipolygon = new Multipolygon(r));
             }
         }
@@ -209,11 +208,4 @@
 
     @Override
-    public void zoomChanged(/*NavigatableComponent source*/) {
-        // TODO Change zoomChanged() method to add a "NavigatableComponent source" argument ? (this method is however used at least by one plugin)
-        //clear(source);
-        clear();
-    }
-
-    @Override
     public void projectionChanged(Projection oldValue, Projection newValue) {
         clear();
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(revision 4626)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(revision 4627)
@@ -21,4 +21,5 @@
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData.Intersection;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.Test;
@@ -134,5 +135,5 @@
             checkMembersAndRoles(r);
 
-            Multipolygon polygon = new Multipolygon(Main.map.mapView, r);
+            Multipolygon polygon = MultipolygonCache.getInstance().get(Main.map.mapView, r);
 
             boolean hasOuterWay = false;
Index: /trunk/src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 4626)
+++ /trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 4627)
@@ -239,7 +239,6 @@
         });
         
-        // Add Multipolygon cache to layer and zoom listeners
+        // Add Multipolygon cache to layer listeners
         addLayerChangeListener(MultipolygonCache.getInstance());
-        addZoomChangeListener(MultipolygonCache.getInstance());
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 4626)
+++ /trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 4627)
@@ -7,4 +7,5 @@
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
 import java.util.ArrayList;
@@ -61,4 +62,7 @@
     public static final IntegerProperty PROP_SNAP_DISTANCE = new IntegerProperty("mappaint.node.snap-distance", 10);
 
+    public static final String PROPNAME_CENTER = "center";
+    public static final String PROPNAME_SCALE  = "scale";
+    
     /**
      * the zoom listeners
@@ -229,4 +233,9 @@
     }
 
+    public AffineTransform getAffineTransform() {
+        return new AffineTransform(
+                1.0/scale, 0.0, 0.0, -1.0/scale, getWidth()/2.0 - center.east()/scale, getHeight()/2.0 + center.north()/scale);
+    }
+    
     /**
      * Return the point on the screen where this Coordinate would be.
@@ -335,10 +344,10 @@
             EastNorth oldCenter = center;
             center = newCenter;
-            firePropertyChange("center", oldCenter, newCenter);
+            firePropertyChange(PROPNAME_CENTER, oldCenter, newCenter);
         }
         if (scale != newScale) {
             double oldScale = scale;
             scale = newScale;
-            firePropertyChange("scale", oldScale, newScale);
+            firePropertyChange(PROPNAME_SCALE, oldScale, newScale);
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 4626)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 4627)
@@ -50,4 +50,5 @@
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
+import org.openstreetmap.josm.gui.NavigatableComponent;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
@@ -790,5 +791,5 @@
 
     public void propertyChange(PropertyChangeEvent evt) {
-        if ("center".equals(evt.getPropertyName()) || "scale".equals(evt.getPropertyName())) {
+        if (NavigatableComponent.PROPNAME_CENTER.equals(evt.getPropertyName()) || NavigatableComponent.PROPNAME_SCALE.equals(evt.getPropertyName())) {
             updateOffscreenBuffer = true;
         }
