Index: src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 13193)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(working copy)
@@ -79,7 +79,6 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.io.GpxReader;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.tools.ExifReader;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.JosmRuntimeException;
@@ -458,8 +457,7 @@
             imgList.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
             imgList.getSelectionModel().addListSelectionListener(evt -> {
                 int index = imgList.getSelectedIndex();
-                Integer orientation = ExifReader.readOrientation(yLayer.data.get(index).getFile());
-                imgDisp.setImage(yLayer.data.get(index).getFile(), orientation);
+                imgDisp.setImage(yLayer.data.get(index));
                 Date date = yLayer.data.get(index).getExifTime();
                 if (date != null) {
                     DateFormat df = DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM);
@@ -482,12 +480,11 @@
                         JpgImporter.FILE_FILTER_WITH_FOLDERS, JFileChooser.FILES_ONLY, "geoimage.lastdirectory");
                 if (fc == null)
                     return;
-                File sel = fc.getSelectedFile();
+                ImageEntry entry = new ImageEntry(fc.getSelectedFile());
+                entry.extractExif();
+                imgDisp.setImage(entry);
 
-                Integer orientation = ExifReader.readOrientation(sel);
-                imgDisp.setImage(sel, orientation);
-
-                Date date = ExifReader.readTime(sel);
+                Date date = entry.getExifTime();
                 if (date != null) {
                     lbExifTime.setText(DateUtils.getDateTimeFormat(DateFormat.SHORT, DateFormat.MEDIUM).format(date));
                     tfGpsTime.setText(DateUtils.getDateFormat(DateFormat.SHORT).format(date)+' ');
Index: src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(revision 13193)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java	(working copy)
@@ -45,7 +45,7 @@
 public class ImageDisplay extends JComponent implements PreferenceChangedListener {
 
     /** The file that is currently displayed */
-    private File file;
+    private ImageEntry entry;
 
     /** The image currently displayed */
     private transient Image image;
@@ -90,8 +90,12 @@
     private static double bilinUpper;
     private static double bilinLower;
 
-    @Override
-    public void preferenceChanged(PreferenceChangeEvent e) {
+    /**
+     * Avoid find-bugs bad style warnings by write accessing static members from
+     * a static method only.
+     * @param e {@code PreferenceChangeEvent}
+     */
+    public static synchronized void preferenceChangedImpl(PreferenceChangeEvent e) {
         if (e == null ||
             e.getKey().equals(AGPIFO_STYLE.getKey())) {
             dragButton = AGPIFO_STYLE.get() ? 1 : 3;
@@ -106,6 +110,11 @@
         }
     }
 
+    @Override
+    public void preferenceChanged(PreferenceChangeEvent e) {
+        preferenceChangedImpl(e);
+    }
+
     /**
      * Manage the visible rectangle of an image with full bounds stored in init.
      * @since 13127
@@ -214,63 +223,81 @@
     /** The thread that reads the images. */
     private class LoadImageRunnable implements Runnable, ImageObserver {
 
+        private final ImageEntry entry;
         private final File file;
-        private final int orientation;
-        private int width;
-        private int height;
 
-        LoadImageRunnable(File file, Integer orientation) {
-            this.file = file;
-            this.orientation = orientation == null ? -1 : orientation;
+        LoadImageRunnable(ImageEntry entry) {
+            this.entry = entry;
+            this.file = entry.getFile();
         }
 
         @Override
         public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
             if (((infoflags & ImageObserver.WIDTH) == ImageObserver.WIDTH) &&
                 ((infoflags & ImageObserver.HEIGHT) == ImageObserver.HEIGHT)) {
-                this.width = width;
-                this.height = height;
-                synchronized (this) {
-                    this.notify();
+                synchronized (entry) {
+                    entry.setWidth(width);
+                    entry.setHeight(height);
+                    entry.notifyAll();
                     return false;
                 }
             }
             return true;
         }
 
-        @Override
-        public void run() {
-            Image img = Toolkit.getDefaultToolkit().createImage(file.getPath());
+        private boolean updateImageEntry(Image img) {
+            if (!(entry.getWidth() > 0 && entry.getHeight() > 0)) {
+                synchronized (entry) {
+                    img.getWidth(this);
+                    img.getHeight(this);
 
-            synchronized (this) {
-                width = -1;
-                img.getWidth(this);
-                img.getHeight(this);
-
-                while (width < 0) {
-                    try {
-                        this.wait();
-                        if (width < 0) {
-                            errorLoading = true;
-                            return;
+                    long now = System.currentTimeMillis();
+                    while (!(entry.getWidth() > 0 && entry.getHeight() > 0)) {
+                        try {
+                            entry.wait(1000);
+                            if (this.entry != ImageDisplay.this.entry)
+                                return false;
+                            if (System.currentTimeMillis() - now > 10000)
+                                synchronized (ImageDisplay.this) {
+                                    errorLoading = true;
+                                    ImageDisplay.this.repaint();
+                                    return false;
+                                }
+                        } catch (InterruptedException e) {
+                            Logging.trace(e);
+                            Logging.warn("InterruptedException in {0} while getting properties of image {1}",
+                                    getClass().getSimpleName(), file.getPath());
+                            Thread.currentThread().interrupt();
                         }
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
                     }
                 }
             }
+            return true;
+        }
+
+        private boolean mayFitMemory(long amountWanted) {
+            return amountWanted < (
+                   Runtime.getRuntime().maxMemory() -
+                   Runtime.getRuntime().totalMemory() +
+                   Runtime.getRuntime().freeMemory());
+        }
+
+        @Override
+        public void run() {
+            Image img = Toolkit.getDefaultToolkit().createImage(file.getPath());
+            if (!updateImageEntry(img))
+                return;
 
-            long allocatedMem = Runtime.getRuntime().totalMemory() -
-                    Runtime.getRuntime().freeMemory();
-            long mem = Runtime.getRuntime().maxMemory()-allocatedMem;
+            int width = entry.getWidth();
+            int height = entry.getHeight();
 
-            if (mem > ((long) width*height*4)*2) {
+            if (mayFitMemory(((long) width)*height*4*2)) {
                 Logging.info("Loading {0} using default toolkit", file.getPath());
                 tracker.addImage(img, 1);
 
                 // Wait for the end of loading
                 while (!tracker.checkID(1, true)) {
-                    if (this.file != ImageDisplay.this.file) {
+                    if (this.entry != ImageDisplay.this.entry) {
                         // The file has changed
                         tracker.removeImage(img);
                         return;
@@ -279,26 +306,21 @@
                         Thread.sleep(5);
                     } catch (InterruptedException e) {
                         Logging.trace(e);
-                        Logging.warn("InterruptedException in "+getClass().getSimpleName()+
-                                " while loading image "+file.getPath());
+                        Logging.warn("InterruptedException in {0} while loading image {1}",
+                                getClass().getSimpleName(), file.getPath());
                         Thread.currentThread().interrupt();
                     }
                 }
                 if (tracker.isErrorID(1)) {
+                    // the tracker catches OutOfMemory conditions
                     img = null;
-                    System.gc();
                 }
             } else {
                 img = null;
             }
 
-            if (img == null || width <= 0 || height <= 0) {
-                tracker.removeImage(img);
-                img = null;
-            }
-
             synchronized (ImageDisplay.this) {
-                if (this.file != ImageDisplay.this.file) {
+                if (this.entry != ImageDisplay.this.entry) {
                     // The file has changed
                     tracker.removeImage(img);
                     return;
@@ -306,15 +328,17 @@
 
                 if (img != null) {
                     boolean switchedDim = false;
-                    if (ExifReader.orientationNeedsCorrection(orientation)) {
-                        if (ExifReader.orientationSwitchesDimensions(orientation)) {
+                    if (ExifReader.orientationNeedsCorrection(entry.getExifOrientation())) {
+                        if (ExifReader.orientationSwitchesDimensions(entry.getExifOrientation())) {
                             width = img.getHeight(null);
                             height = img.getWidth(null);
                             switchedDim = true;
                         }
                         final BufferedImage rot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
-                        final AffineTransform xform = ExifReader.getRestoreOrientationTransform(orientation,
-                                img.getWidth(null), img.getHeight(null));
+                        final AffineTransform xform = ExifReader.getRestoreOrientationTransform(
+                                entry.getExifOrientation(),
+                                img.getWidth(null),
+                                img.getHeight(null));
                         final Graphics2D g = rot.createGraphics();
                         g.drawImage(img, xform, null);
                         g.dispose();
@@ -325,8 +349,8 @@
                     ImageDisplay.this.image = img;
                     visibleRect = new VisRect(0, 0, width, height);
 
-                    Logging.info("Loaded {0} with dimensions {1}x{2} mem(prev-avail={3}m,taken={4}m) exifOrientationSwitchedDimension={5}",
-                            file.getPath(), width, height, mem/1024/1024, width*height*4/1024/1024, switchedDim);
+                    Logging.info("Loaded {0} with dimensions {1}x{2} memoryTaken={3}m exifOrientationSwitchedDimension={4}",
+                            file.getPath(), width, height, width*height*4/1024/1024, switchedDim);
                 }
 
                 selectedRect = null;
@@ -360,12 +384,12 @@
         }
 
         private void mouseWheelMovedImpl(int x, int y, int rotation, boolean refreshMousePointInImg) {
-            File file;
+            ImageEntry entry;
             Image image;
             VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
                 visibleRect = ImageDisplay.this.visibleRect;
             }
@@ -417,7 +441,7 @@
             visibleRect.checkRectPos();
 
             synchronized (ImageDisplay.this) {
-                if (ImageDisplay.this.file == file) {
+                if (ImageDisplay.this.entry == entry) {
                     ImageDisplay.this.visibleRect = visibleRect;
                 }
             }
@@ -446,12 +470,12 @@
         @Override
         public void mouseClicked(MouseEvent e) {
             // Move the center to the clicked point.
-            File file;
+            ImageEntry entry;
             Image image;
             VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
                 visibleRect = ImageDisplay.this.visibleRect;
             }
@@ -485,7 +509,7 @@
             visibleRect.checkRectPos();
 
             synchronized (ImageDisplay.this) {
-                if (ImageDisplay.this.file == file) {
+                if (ImageDisplay.this.entry == entry) {
                     ImageDisplay.this.visibleRect = visibleRect;
                 }
             }
@@ -518,12 +542,12 @@
             if (!mouseIsDragging(e) && !mouseIsZoomSelecting(e))
                 return;
 
-            File file;
+            ImageEntry entry;
             Image image;
             VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
                 visibleRect = ImageDisplay.this.visibleRect;
             }
@@ -538,7 +562,7 @@
                 visibleRect.y += mousePointInImg.y - p.y;
                 visibleRect.checkRectPos();
                 synchronized (ImageDisplay.this) {
-                    if (ImageDisplay.this.file == file) {
+                    if (ImageDisplay.this.entry == entry) {
                         ImageDisplay.this.visibleRect = visibleRect;
                     }
                 }
@@ -564,12 +588,14 @@
 
         @Override
         public void mouseReleased(MouseEvent e) {
-            File file;
+            ImageEntry entry;
             Image image;
+            VisRect visibleRect;
 
             synchronized (ImageDisplay.this) {
-                file = ImageDisplay.this.file;
+                entry = ImageDisplay.this.entry;
                 image = ImageDisplay.this.image;
+                visibleRect = ImageDisplay.this.visibleRect;
             }
 
             if (image == null)
@@ -613,7 +639,7 @@
             }
 
             synchronized (ImageDisplay.this) {
-                if (file == ImageDisplay.this.file) {
+                if (entry == ImageDisplay.this.entry) {
                     if (selectedRect == null) {
                         ImageDisplay.this.visibleRect = visibleRect;
                     } else {
@@ -655,18 +681,17 @@
 
     /**
      * Sets a new source image to be displayed by this {@code ImageDisplay}.
-     * @param file new source image
-     * @param orientation orientation of new source (landscape, portrait, upside-down, etc.)
+     * @param entry new source image
      */
-    public void setImage(File file, Integer orientation) {
+    public void setImage(ImageEntry entry) {
         synchronized (this) {
-            this.file = file;
+            this.entry = entry;
             image = null;
             errorLoading = false;
         }
         repaint();
-        if (file != null) {
-            new Thread(new LoadImageRunnable(file, orientation), LoadImageRunnable.class.getName()).start();
+        if (entry != null) {
+            new Thread(new LoadImageRunnable(entry), LoadImageRunnable.class.getName()).start();
         }
     }
 
@@ -681,14 +706,14 @@
 
     @Override
     public void paintComponent(Graphics g) {
+        ImageEntry entry;
         Image image;
-        File file;
         VisRect visibleRect;
         boolean errorLoading;
 
         synchronized (this) {
             image = this.image;
-            file = this.file;
+            entry = this.entry;
             visibleRect = this.visibleRect;
             errorLoading = this.errorLoading;
         }
@@ -698,7 +723,7 @@
         }
 
         Dimension size = getSize();
-        if (file == null) {
+        if (entry == null) {
             g.setColor(Color.black);
             String noImageStr = tr("No image");
             Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(noImageStr, g);
@@ -709,9 +734,9 @@
             g.setColor(Color.black);
             String loadingStr;
             if (!errorLoading) {
-                loadingStr = tr("Loading {0}", file.getName());
+                loadingStr = tr("Loading {0}", entry.getFile().getName());
             } else {
-                loadingStr = tr("Error on file {0}", file.getName());
+                loadingStr = tr("Error on file {0}", entry.getFile().getName());
             }
             Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(loadingStr, g);
             g.drawString(loadingStr,
@@ -771,7 +796,7 @@
                 g.drawRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
             }
             if (errorLoading) {
-                String loadingStr = tr("Error on file {0}", file.getName());
+                String loadingStr = tr("Error on file {0}", entry.getFile().getName());
                 Rectangle2D noImageSize = g.getFontMetrics(g.getFont()).getStringBounds(loadingStr, g);
                 g.drawString(loadingStr,
                         (int) ((size.width - noImageSize.getWidth()) / 2),
@@ -884,12 +909,12 @@
      * the component size.
      */
     public void zoomBestFitOrOne() {
-        File file;
+        ImageEntry entry;
         Image image;
         VisRect visibleRect;
 
         synchronized (this) {
-            file = this.file;
+            entry = this.entry;
             image = this.image;
             visibleRect = this.visibleRect;
         }
@@ -910,7 +935,7 @@
         }
 
         synchronized (this) {
-            if (file == this.file) {
+            if (this.entry == entry) {
                 this.visibleRect = visibleRect;
             }
         }
Index: src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 13193)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(working copy)
@@ -20,6 +20,7 @@
 import com.drew.metadata.MetadataException;
 import com.drew.metadata.exif.ExifIFD0Directory;
 import com.drew.metadata.exif.GpsDirectory;
+import com.drew.metadata.jpeg.JpegDirectory;
 
 /**
  * Stores info about each image
@@ -52,6 +53,9 @@
     /** The time after correlation with a gpx track */
     private Date gpsTime;
 
+    private int width;
+    private int height;
+
     /**
      * When the correlation dialog is open, we like to show the image position
      * for the current time offset on the map in real time.
@@ -76,6 +80,20 @@
     }
 
     /**
+     * @return width of the image this ImageEntry represents
+     */
+    public int getWidth() {
+        return width;
+    }
+
+    /**
+     * @return height of the image this ImageEntry represents
+     */
+    public int getHeight() {
+        return height;
+    }
+
+    /**
      * Returns the position value. The position value from the temporary copy
      * is returned if that copy exists.
      * @return the position value
@@ -141,7 +159,7 @@
      * @return EXIF orientation
      */
     public Integer getExifOrientation() {
-        return exifOrientation;
+        return exifOrientation != null ? exifOrientation : 1;
     }
 
     /**
@@ -230,6 +248,20 @@
     }
 
     /**
+     * @param width set the width of this ImageEntry
+     */
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    /**
+     * @param height set the height of this ImageEntry
+     */
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    /**
      * Sets the position.
      * @param pos cached position
      */
@@ -456,6 +488,7 @@
             setExifTime(null);
         }
 
+        final Directory dir = metadata.getFirstDirectoryOfType(JpegDirectory.class);
         final Directory dirExif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
         final GpsDirectory dirGps = metadata.getFirstDirectoryOfType(GpsDirectory.class);
 
@@ -468,6 +501,18 @@
             Logging.debug(ex);
         }
 
+        try {
+            if (dir != null) {
+                // there are cases where these do not match width and height stored in dirExif
+                int width = dir.getInt(JpegDirectory.TAG_IMAGE_WIDTH);
+                int height = dir.getInt(JpegDirectory.TAG_IMAGE_HEIGHT);
+                setWidth(width);
+                setHeight(height);
+            }
+        } catch (MetadataException ex) {
+            Logging.debug(ex);
+        }
+
         if (dirGps == null) {
             setExifCoor(null);
             setPos(null);
Index: src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 13193)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(working copy)
@@ -322,7 +322,7 @@
             if (imageChanged) {
                 // Set only if the image is new to preserve zoom and position if the same image is redisplayed
                 // (e.g. to update the OSD).
-                imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
+                imgDisplay.setImage(entry);
             }
             setTitle(tr("Geotagged Images") + (entry.getFile() != null ? " - " + entry.getFile().getName() : ""));
             StringBuilder osd = new StringBuilder(entry.getFile() != null ? entry.getFile().getName() : "");
@@ -355,7 +355,7 @@
             // if this method is called to reinitialize dialog content with a blank image,
             // do not actually show the dialog again with a blank image if currently hidden (fix #10672)
             setTitle(tr("Geotagged Images"));
-            imgDisplay.setImage(null, null);
+            imgDisplay.setImage(null);
             imgDisplay.setOsdText("");
             return;
         }
