Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 13037)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 13038)
@@ -13,4 +13,5 @@
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.awt.RenderingHints;
 import java.awt.Toolkit;
 import java.awt.event.MouseEvent;
@@ -28,4 +29,5 @@
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.ExifReader;
+import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Logging;
 
@@ -461,4 +463,8 @@
     }
 
+    /**
+     * Sets the On-Screen-Display text.
+     * @param text text to display on top of the image
+     */
     public void setOsdText(String text) {
         this.osdText = text;
@@ -478,4 +484,8 @@
             visibleRect = this.visibleRect;
             errorLoading = this.errorLoading;
+        }
+
+        if (g instanceof Graphics2D) {
+            ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
         }
 
@@ -502,8 +512,18 @@
         } else {
             Rectangle target = calculateDrawImageRectangle(visibleRect, size);
-            g.drawImage(image,
-                    target.x, target.y, target.x + target.width, target.y + target.height,
-                    visibleRect.x, visibleRect.y, visibleRect.x + visibleRect.width, visibleRect.y + visibleRect.height,
-                    null);
+            // See https://community.oracle.com/docs/DOC-983611 - The Perils of Image.getScaledInstance()
+            // Pre-scale image when downscaling by more than two times to avoid aliasing from default algorithm
+            if (selectedRect == null && (target.width < visibleRect.width/2 || target.height < visibleRect.height/2)) {
+                BufferedImage buffImage = ImageProvider.toBufferedImage(image);
+                g.drawImage(ImageProvider.createScaledImage(buffImage, target.width, target.height, RenderingHints.VALUE_INTERPOLATION_BILINEAR),
+                        target.x, target.y, target.x + target.width, target.y + target.height,
+                        visibleRect.x, visibleRect.y, visibleRect.x + target.width, visibleRect.y + target.height,
+                        null);
+            } else {
+                g.drawImage(image,
+                        target.x, target.y, target.x + target.width, target.y + target.height,
+                        visibleRect.x, visibleRect.y, visibleRect.x + visibleRect.width, visibleRect.y + visibleRect.height,
+                        null);
+            }
             if (selectedRect != null) {
                 Point topLeft = img2compCoord(visibleRect, selectedRect.x, selectedRect.y, size);
Index: trunk/src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 13037)
+++ trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 13038)
@@ -1394,4 +1394,52 @@
 
     /**
+     * Returns a scaled instance of the provided {@code BufferedImage}.
+     * This method will use a multi-step scaling technique that provides higher quality than the usual
+     * one-step technique (only useful in downscaling cases, where {@code targetWidth} or {@code targetHeight} is
+     * smaller than the original dimensions, and generally only when the {@code BILINEAR} hint is specified).
+     *
+     * From https://community.oracle.com/docs/DOC-983611: "The Perils of Image.getScaledInstance()"
+     *
+     * @param img the original image to be scaled
+     * @param targetWidth the desired width of the scaled instance, in pixels
+     * @param targetHeight the desired height of the scaled instance, in pixels
+     * @param hint one of the rendering hints that corresponds to
+     * {@code RenderingHints.KEY_INTERPOLATION} (e.g.
+     * {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
+     * {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
+     * {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
+     * @return a scaled version of the original {@code BufferedImage}
+     * @since 13038
+     */
+    public static BufferedImage createScaledImage(BufferedImage img, int targetWidth, int targetHeight, Object hint) {
+        int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
+        // start with original size, then scale down in multiple passes with drawImage() until the target size is reached
+        BufferedImage ret = img;
+        int w = img.getWidth(null);
+        int h = img.getHeight(null);
+        do {
+            if (w > targetWidth) {
+                w /= 2;
+                if (w < targetWidth) {
+                    w = targetWidth;
+                }
+            }
+            if (h > targetHeight) {
+                h /= 2;
+                if (h < targetHeight) {
+                    h = targetHeight;
+                }
+            }
+            BufferedImage tmp = new BufferedImage(w, h, type);
+            Graphics2D g2 = tmp.createGraphics();
+            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
+            g2.drawImage(ret, 0, 0, w, h, null);
+            g2.dispose();
+            ret = tmp;
+        } while (w != targetWidth || h != targetHeight);
+        return ret;
+    }
+
+    /**
      * Replies the icon for an OSM primitive type
      * @param type the type
@@ -1910,3 +1958,21 @@
         }
     }
+
+    /**
+     * Converts an {@link Image} to a {@link BufferedImage} instance.
+     * @param image image to convert
+     * @return a {@code BufferedImage} instance for the given {@code Image}.
+     * @since 13038
+     */
+    public static BufferedImage toBufferedImage(Image image) {
+        if (image instanceof BufferedImage) {
+            return (BufferedImage) image;
+        } else {
+            BufferedImage buffImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
+            Graphics2D g2 = buffImage.createGraphics();
+            g2.drawImage(image, 0, 0, null);
+            g2.dispose();
+            return buffImage;
+        }
+    }
 }
