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}