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}