001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.geoimage;
003
004import java.awt.Dimension;
005import java.awt.image.BufferedImage;
006import java.io.File;
007import java.io.IOException;
008import java.io.UncheckedIOException;
009import java.util.ArrayList;
010import java.util.Collection;
011
012import org.apache.commons.jcs3.access.behavior.ICacheAccess;
013import org.apache.commons.jcs3.engine.behavior.ICache;
014import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
015import org.openstreetmap.josm.data.cache.JCSCacheManager;
016import org.openstreetmap.josm.gui.MainApplication;
017import org.openstreetmap.josm.spi.preferences.Config;
018import org.openstreetmap.josm.tools.Logging;
019import org.openstreetmap.josm.tools.Stopwatch;
020
021/**
022 * Loads thumbnail previews for a list of images from a {@link GeoImageLayer}.
023 *
024 * Thumbnails are loaded in the background and cached on disk for the next session.
025 */
026public class ThumbsLoader implements Runnable {
027    public static final int maxSize = 120;
028    public static final int minSize = 22;
029    public volatile boolean stop;
030    private final Collection<ImageEntry> data;
031    private final GeoImageLayer layer;
032    private ICacheAccess<String, BufferedImageCacheEntry> cache;
033    private final boolean cacheOff = Config.getPref().getBoolean("geoimage.noThumbnailCache", false);
034
035    private ThumbsLoader(Collection<ImageEntry> data, GeoImageLayer layer) {
036        this.data = data;
037        this.layer = layer;
038        initCache();
039    }
040
041    /**
042     * Constructs a new thumbnail loader that operates on a geoimage layer.
043     * @param layer geoimage layer
044     */
045    public ThumbsLoader(GeoImageLayer layer) {
046        this(new ArrayList<>(layer.getImageData().getImages()), layer);
047    }
048
049    /**
050     * Constructs a new thumbnail loader that operates on the image entries
051     * @param entries image entries
052     */
053    public ThumbsLoader(Collection<ImageEntry> entries) {
054        this(entries, null);
055    }
056
057    /**
058     * Initialize the thumbnail cache.
059     */
060    private void initCache() {
061        if (!cacheOff) {
062            cache = JCSCacheManager.getCache("geoimage-thumbnails", 0, 120,
063                    Config.getDirs().getCacheDirectory(true).getPath() + File.separator + "geoimage-thumbnails");
064        }
065    }
066
067    @Override
068    public void run() {
069        int count = 0;
070        Stopwatch stopwatch = Stopwatch.createStarted();
071        Logging.debug("Loading {0} thumbnails", data.size());
072        for (ImageEntry entry : data) {
073            if (stop) return;
074
075            // Do not load thumbnails that were loaded before.
076            if (!entry.hasThumbnail()) {
077                entry.setThumbnail(loadThumb(entry));
078
079                if (layer != null && MainApplication.isDisplayingMapView()) {
080                    layer.updateBufferAndRepaint();
081                }
082            }
083            count++;
084        }
085        Logging.debug("Loaded {0} thumbnails in {1}", count, stopwatch);
086        if (layer != null) {
087            layer.thumbsLoaded();
088            layer.updateBufferAndRepaint();
089        }
090    }
091
092    private BufferedImage loadThumb(ImageEntry entry) {
093        final String cacheIdent = entry.getFile().toString() + ICache.NAME_COMPONENT_DELIMITER + maxSize;
094
095        if (!cacheOff && cache != null) {
096            try {
097                BufferedImageCacheEntry cacheEntry = cache.get(cacheIdent);
098                if (cacheEntry != null && cacheEntry.getImage() != null) {
099                    Logging.debug("{0} from cache", cacheIdent);
100                    return cacheEntry.getImage();
101                }
102            } catch (IOException e) {
103                Logging.warn(e);
104            }
105        }
106
107        BufferedImage img;
108        try {
109            img = entry.read(new Dimension(maxSize, maxSize));
110        } catch (IOException e) {
111            Logging.warn("Failed to load geoimage thumb");
112            Logging.warn(e);
113            return null;
114        }
115
116        if (img == null || img.getWidth() <= 0 || img.getHeight() <= 0) {
117            Logging.error(" Invalid image");
118            return null;
119        }
120
121        if (!cacheOff && cache != null) {
122            try {
123                cache.put(cacheIdent, BufferedImageCacheEntry.pngEncoded(img));
124            } catch (UncheckedIOException e) {
125                Logging.warn("Failed to save geoimage thumb to cache");
126                Logging.warn(e);
127            }
128        }
129
130        return img;
131    }
132}