source: josm/trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java@ 12460

Last change on this file since 12460 was 12460, checked in by bastiK, 7 years ago

see #14794 - javadoc

  • Property svn:eol-style set to native
File size: 6.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer.geoimage;
3
4import java.awt.Graphics2D;
5import java.awt.Image;
6import java.awt.MediaTracker;
7import java.awt.Rectangle;
8import java.awt.Toolkit;
9import java.awt.geom.AffineTransform;
10import java.awt.image.BufferedImage;
11import java.io.ByteArrayOutputStream;
12import java.io.File;
13import java.io.IOException;
14import java.util.ArrayList;
15import java.util.Collection;
16
17import javax.imageio.ImageIO;
18
19import org.apache.commons.jcs.access.behavior.ICacheAccess;
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
22import org.openstreetmap.josm.data.cache.JCSCacheManager;
23import org.openstreetmap.josm.tools.ExifReader;
24
25/**
26 * Loads thumbnail previews for a list of images from a {@link GeoImageLayer}.
27 *
28 * Thumbnails are loaded in the background and cached on disk for the next session.
29 */
30public class ThumbsLoader implements Runnable {
31 public static final int maxSize = 120;
32 public static final int minSize = 22;
33 public volatile boolean stop;
34 private final Collection<ImageEntry> data;
35 private final GeoImageLayer layer;
36 private MediaTracker tracker;
37 private ICacheAccess<String, BufferedImageCacheEntry> cache;
38 private final boolean cacheOff = Main.pref.getBoolean("geoimage.noThumbnailCache", false);
39
40 private ThumbsLoader(Collection<ImageEntry> data, GeoImageLayer layer) {
41 this.data = data;
42 this.layer = layer;
43 initCache();
44 }
45
46 /**
47 * Constructs a new thumbnail loader that operates on a geoimage layer.
48 * @param layer geoimage layer
49 */
50 public ThumbsLoader(GeoImageLayer layer) {
51 this(new ArrayList<>(layer.data), layer);
52 }
53
54 /**
55 * Constructs a new thumbnail loader that operates on the image entries
56 * @param entries image entries
57 */
58 public ThumbsLoader(Collection<ImageEntry> entries) {
59 this(entries, null);
60 }
61
62 /**
63 * Initialize the thumbnail cache.
64 */
65 private void initCache() {
66 if (!cacheOff) {
67 try {
68 cache = JCSCacheManager.getCache("geoimage-thumbnails", 0, 120,
69 Main.pref.getCacheDirectory().getPath() + File.separator + "geoimage-thumbnails");
70 } catch (IOException e) {
71 Main.warn("Failed to initialize cache for geoimage-thumbnails");
72 Main.warn(e);
73 }
74 }
75 }
76
77 @Override
78 public void run() {
79 Main.debug("Load Thumbnails");
80 tracker = new MediaTracker(Main.map.mapView);
81 for (ImageEntry entry : data) {
82 if (stop) return;
83
84 // Do not load thumbnails that were loaded before.
85 if (!entry.hasThumbnail()) {
86 entry.setThumbnail(loadThumb(entry));
87
88 if (layer != null && Main.isDisplayingMapView()) {
89 layer.updateBufferAndRepaint();
90 }
91 }
92 }
93 if (layer != null) {
94 layer.thumbsLoaded();
95 layer.updateBufferAndRepaint();
96 }
97 }
98
99 private BufferedImage loadThumb(ImageEntry entry) {
100 final String cacheIdent = entry.getFile().toString()+':'+maxSize;
101
102 if (!cacheOff && cache != null) {
103 try {
104 BufferedImageCacheEntry cacheEntry = cache.get(cacheIdent);
105 if (cacheEntry != null && cacheEntry.getImage() != null) {
106 Main.debug(" from cache");
107 return cacheEntry.getImage();
108 }
109 } catch (IOException e) {
110 Main.warn(e);
111 }
112 }
113
114 Image img = Toolkit.getDefaultToolkit().createImage(entry.getFile().getPath());
115 tracker.addImage(img, 0);
116 try {
117 tracker.waitForID(0);
118 } catch (InterruptedException e) {
119 Main.error(" InterruptedException while loading thumb");
120 Thread.currentThread().interrupt();
121 return null;
122 }
123 if (tracker.isErrorID(1) || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) {
124 Main.error(" Invalid image");
125 return null;
126 }
127
128 final int w = img.getWidth(null);
129 final int h = img.getHeight(null);
130 final int hh, ww;
131 final Integer exifOrientation = entry.getExifOrientation();
132 if (exifOrientation != null && ExifReader.orientationSwitchesDimensions(exifOrientation)) {
133 ww = h;
134 hh = w;
135 } else {
136 ww = w;
137 hh = h;
138 }
139
140 Rectangle targetSize = ImageDisplay.calculateDrawImageRectangle(
141 new Rectangle(0, 0, ww, hh),
142 new Rectangle(0, 0, maxSize, maxSize));
143 BufferedImage scaledBI = new BufferedImage(targetSize.width, targetSize.height, BufferedImage.TYPE_INT_RGB);
144 Graphics2D g = scaledBI.createGraphics();
145
146 final AffineTransform scale = AffineTransform.getScaleInstance((double) targetSize.width / ww, (double) targetSize.height / hh);
147 if (exifOrientation != null) {
148 final AffineTransform restoreOrientation = ExifReader.getRestoreOrientationTransform(exifOrientation, w, h);
149 scale.concatenate(restoreOrientation);
150 }
151
152 while (!g.drawImage(img, scale, null)) {
153 try {
154 Thread.sleep(10);
155 } catch (InterruptedException e) {
156 Main.warn("InterruptedException while drawing thumb");
157 Thread.currentThread().interrupt();
158 }
159 }
160 g.dispose();
161 tracker.removeImage(img);
162
163 if (scaledBI.getWidth() <= 0 || scaledBI.getHeight() <= 0) {
164 Main.error(" Invalid image");
165 return null;
166 }
167
168 if (!cacheOff && cache != null) {
169 try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
170 ImageIO.write(scaledBI, "png", output);
171 cache.put(cacheIdent, new BufferedImageCacheEntry(output.toByteArray()));
172 } catch (IOException e) {
173 Main.warn("Failed to save geoimage thumb to cache");
174 Main.warn(e);
175 }
176 }
177
178 return scaledBI;
179 }
180}
Note: See TracBrowser for help on using the repository browser.