001// License: GPL. See LICENSE file for details. 002package org.openstreetmap.josm.gui.layer.geoimage; 003 004import java.awt.Graphics2D; 005import java.awt.Image; 006import java.awt.MediaTracker; 007import java.awt.Rectangle; 008import java.awt.Toolkit; 009import java.awt.geom.AffineTransform; 010import java.awt.image.BufferedImage; 011import java.util.ArrayList; 012import java.util.List; 013 014import org.openstreetmap.josm.Main; 015import org.openstreetmap.josm.io.CacheFiles; 016import org.openstreetmap.josm.tools.ExifReader; 017 018public class ThumbsLoader implements Runnable { 019 public static final int maxSize = 120; 020 public static final int minSize = 22; 021 public volatile boolean stop = false; 022 List<ImageEntry> data; 023 GeoImageLayer layer; 024 MediaTracker tracker; 025 CacheFiles cache; 026 boolean cacheOff = Main.pref.getBoolean("geoimage.noThumbnailCache", false); 027 028 public ThumbsLoader(GeoImageLayer layer) { 029 this.layer = layer; 030 this.data = new ArrayList<>(layer.data); 031 if (!cacheOff) { 032 cache = new CacheFiles("geoimage-thumbnails", false); 033 cache.setExpire(CacheFiles.EXPIRE_NEVER, false); 034 cache.setMaxSize(120, false); 035 } 036 } 037 038 @Override 039 public void run() { 040 Main.debug("Load Thumbnails"); 041 tracker = new MediaTracker(Main.map.mapView); 042 for (int i = 0; i < data.size(); i++) { 043 if (stop) return; 044 045 // Do not load thumbnails that were loaded before. 046 if (data.get(i).thumbnail == null) { 047 data.get(i).thumbnail = loadThumb(data.get(i)); 048 049 if (Main.isDisplayingMapView()) { 050 layer.updateOffscreenBuffer = true; 051 Main.map.mapView.repaint(); 052 } 053 } 054 } 055 layer.thumbsLoaded(); 056 layer.updateOffscreenBuffer = true; 057 Main.map.mapView.repaint(); 058 } 059 060 private BufferedImage loadThumb(ImageEntry entry) { 061 final String cacheIdent = entry.getFile().toString()+":"+maxSize; 062 063 if (!cacheOff) { 064 BufferedImage cached = cache.getImg(cacheIdent); 065 if (cached != null) { 066 Main.debug(" from cache"); 067 return cached; 068 } 069 } 070 071 Image img = Toolkit.getDefaultToolkit().createImage(entry.getFile().getPath()); 072 tracker.addImage(img, 0); 073 try { 074 tracker.waitForID(0); 075 } catch (InterruptedException e) { 076 Main.error(" InterruptedException while loading thumb"); 077 return null; 078 } 079 if (tracker.isErrorID(1) || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) { 080 Main.error(" Invalid image"); 081 return null; 082 } 083 084 final int w = img.getWidth(null); 085 final int h = img.getHeight(null); 086 final int hh, ww; 087 if (ExifReader.orientationSwitchesDimensions(entry.getExifOrientation())) { 088 ww = h; 089 hh = w; 090 } else { 091 ww = w; 092 hh = h; 093 } 094 095 Rectangle targetSize = ImageDisplay.calculateDrawImageRectangle( 096 new Rectangle(0, 0, ww, hh), 097 new Rectangle(0, 0, maxSize, maxSize)); 098 BufferedImage scaledBI = new BufferedImage(targetSize.width, targetSize.height, BufferedImage.TYPE_INT_RGB); 099 Graphics2D g = scaledBI.createGraphics(); 100 101 final AffineTransform restoreOrientation = ExifReader.getRestoreOrientationTransform(entry.getExifOrientation(), w, h); 102 final AffineTransform scale = AffineTransform.getScaleInstance((double) targetSize.width / ww, (double) targetSize.height / hh); 103 scale.concatenate(restoreOrientation); 104 105 while (!g.drawImage(img, scale, null)) { 106 try { 107 Thread.sleep(10); 108 } catch(InterruptedException ie) { 109 Main.warn("InterruptedException while drawing thumb"); 110 } 111 } 112 g.dispose(); 113 tracker.removeImage(img); 114 115 if (scaledBI.getWidth() <= 0 || scaledBI.getHeight() <= 0) { 116 Main.error(" Invalid image"); 117 return null; 118 } 119 120 if (!cacheOff) { 121 cache.saveImg(cacheIdent, scaledBI); 122 } 123 124 return scaledBI; 125 } 126 127}