Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8750)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8751)
@@ -135,7 +135,13 @@
 
     /**
+     * Offset between calculated zoom level and zoom level used to download and show tiles. Negative values will result in
+     * lower resolution of imagery useful in "retina" displays, positive values will result in higher resolution
+     */
+    public static final IntegerProperty ZOOM_OFFSET = new IntegerProperty(PREFERENCE_PREFIX + ".zoom_offset", 0);
+
+    /**
      * use fairly small memory cache, as cached objects are quite big, as they contain BufferedImages
      */
-    public static final IntegerProperty MEMORY_CACHE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + "cache.max_objects_ram", 200);
+    public static final IntegerProperty MEMORY_CACHE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + ".cache.max_objects_ram", (int)Math.max(200,  200 * Math.pow(4, ZOOM_OFFSET.get())));
 
     /*
@@ -280,20 +286,19 @@
     protected int getBestZoom() {
         double factor = getScaleFactor(1); // check the ratio between area of tilesize at zoom 1 to current view
-        double result = Math.log(factor)/Math.log(2)/2+1;
+        double result = Math.log(factor)/Math.log(2)/2;
         /*
          * Math.log(factor)/Math.log(2) - gives log base 2 of factor
          * We divide result by 2, as factor contains ratio between areas. We could do Math.sqrt before log, or just divide log by 2
-         * In general, smaller zoom levels are more readable.  We prefer big,
-         * block, pixelated (but readable) map text to small, smeared,
-         * unreadable underzoomed text.  So, use .floor() instead of rounding
-         * to skew things a bit toward the lower zooms.
-         * Remember, that result here, should correspond to TMSLayer.paint(...)
-         * getScaleFactor(...) is supposed to be between 0.75 and 3
+         *
+         * ZOOM_OFFSET controls, whether we work with overzoomed or underzoomed tiles. Positive ZOOM_OFFSET
+         * is for working with underzoomed tiles (higher quality when working with aerial imagery), negative ZOOM_OFFSET
+         * is for working with overzoomed tiles (big, pixelated), which is good when working with high-dpi screens and/or
+         * maps as a imagery layer
          */
-        int intResult = (int) Math.floor(result);
-        if (intResult > getMaxZoomLvl())
-            return getMaxZoomLvl();
-        if (intResult < getMinZoomLvl())
-            return getMinZoomLvl();
+
+        int intResult = (int) Math.round(result + 1 + ZOOM_OFFSET.get() / 1.9);
+
+        intResult = Math.min(intResult, getMaxZoomLvl());
+        intResult = Math.max(intResult, getMinZoomLvl());
         return intResult;
     }
@@ -1192,5 +1197,5 @@
 
         private boolean tooLarge() {
-            return this.tilesSpanned() > 10;
+            return this.tilesSpanned() > 20;
         }
 
@@ -1374,8 +1379,5 @@
         int zoom = currentZoomLevel;
         if (autoZoom) {
-            double pixelScaling = getScaleFactor(zoom);
-            if (pixelScaling > 3 || pixelScaling < 0.7) {
-                zoom = getBestZoom();
-            }
+            zoom = getBestZoom();
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CommonSettingsPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CommonSettingsPanel.java	(revision 8750)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CommonSettingsPanel.java	(revision 8751)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
 import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer;
+import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
 import org.openstreetmap.josm.gui.widgets.JosmComboBox;
@@ -39,4 +40,5 @@
     private final JSpinner maxElementsOnDisk;
     private final JSpinner maxElementsInRam;
+    private final JSlider tilesZoom = new JSlider(-2, 2, 0);
 
 
@@ -99,4 +101,12 @@
         add(GBC.glue(5, 0), GBC.std());
         add(this.maxElementsInRam, GBC.eol());
+
+        this.tilesZoom.setPaintLabels(true);
+        this.tilesZoom.setMajorTickSpacing(2);
+        this.tilesZoom.setMinorTickSpacing(1);
+        this.tilesZoom.setPaintTicks(true);
+        add(new JLabel(tr("Tiles zoom offset:")));
+        add(GBC.glue(5, 0), GBC.std());
+        add(this.tilesZoom, GBC.eol());
     }
 
@@ -113,5 +123,5 @@
         this.maxElementsOnDisk.setValue(AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get());
         this.maxElementsInRam.setValue(AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.get());
-
+        this.tilesZoom.setValue(AbstractTileSourceLayer.ZOOM_OFFSET.get());
     }
 
@@ -124,4 +134,5 @@
         ImageryLayer.PROP_FADE_COLOR.put(this.btnFadeColor.getBackground());
         ImageryLayer.PROP_SHARPEN_LEVEL.put(sharpen.getSelectedIndex());
+
         boolean restartRequired = false;
         if (!AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get().equals(this.maxElementsOnDisk.getValue())) {
@@ -138,7 +149,12 @@
         if (!AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.get().equals(this.maxElementsInRam.getValue())) {
             AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.put((Integer) this.maxElementsInRam.getValue());
+            restartRequired = true;
         }
 
-
+        if (!AbstractTileSourceLayer.ZOOM_OFFSET.get().equals(this.tilesZoom.getValue())) {
+            // TODO: make warning about too small MEMORY_CACHE_SIZE?
+            AbstractTileSourceLayer.ZOOM_OFFSET.put(this.tilesZoom.getValue());
+            restartRequired = true;
+        }
         return restartRequired;
     }
