001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.plugins.streetside.actions; 003 004import java.awt.image.BufferedImage; 005import java.text.MessageFormat; 006 007import javax.swing.SwingUtilities; 008 009import org.apache.log4j.Logger; 010import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage; 011import org.openstreetmap.josm.plugins.streetside.StreetsideData; 012import org.openstreetmap.josm.plugins.streetside.StreetsideDataListener; 013import org.openstreetmap.josm.plugins.streetside.StreetsideImage; 014import org.openstreetmap.josm.plugins.streetside.StreetsideLayer; 015import org.openstreetmap.josm.plugins.streetside.cache.CacheUtils; 016import org.openstreetmap.josm.plugins.streetside.gui.StreetsideMainDialog; 017import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties; 018 019 020/** 021 * Thread containing the walk process. 022 * 023 * @author nokutu 024 */ 025public class WalkThread extends Thread implements StreetsideDataListener { 026 private final int interval; 027 private final StreetsideData data; 028 private boolean end; 029 private final boolean waitForFullQuality; 030 private final boolean followSelected; 031 private final boolean goForward; 032 private BufferedImage lastImage; 033 private volatile boolean paused; 034 035 final static Logger logger = Logger.getLogger(WalkThread.class); 036 037 /** 038 * Main constructor. 039 * 040 * @param interval How often the images switch. 041 * @param waitForPicture If it must wait for the full resolution picture or just the 042 * thumbnail. 043 * @param followSelected Zoom to each image that is selected. 044 * @param goForward true to go forward; false to go backwards. 045 */ 046 public WalkThread(int interval, boolean waitForPicture, 047 boolean followSelected, boolean goForward) { 048 this.interval = interval; 049 waitForFullQuality = waitForPicture; 050 this.followSelected = followSelected; 051 this.goForward = goForward; 052 data = StreetsideLayer.getInstance().getData(); 053 data.addListener(this); 054 } 055 056 @Override 057 public void run() { 058 try { 059 while (!end && data.getSelectedImage().next() != null) { 060 StreetsideAbstractImage image = data.getSelectedImage(); 061 if (image != null && image.next() instanceof StreetsideImage) { 062 // Predownload next 10 thumbnails. 063 preDownloadImages((StreetsideImage) image.next(), 10, CacheUtils.PICTURE.THUMBNAIL); 064 if(StreetsideProperties.PREDOWNLOAD_CUBEMAPS.get()) { 065 preDownloadCubemaps((StreetsideImage) image.next(), 10); 066 } 067 if (waitForFullQuality) { 068 // Start downloading 3 next full images. 069 StreetsideAbstractImage currentImage = image.next(); 070 preDownloadImages((StreetsideImage) currentImage, 3, CacheUtils.PICTURE.FULL_IMAGE); 071 } 072 } 073 try { 074 // Waits for full quality picture. 075 final BufferedImage displayImage = StreetsideMainDialog.getInstance().getStreetsideImageDisplay().getImage(); 076 if (waitForFullQuality && image instanceof StreetsideImage) { 077 while (displayImage == lastImage || displayImage == null || displayImage.getWidth() < 2048) { 078 Thread.sleep(100); 079 } 080 } else { // Waits for thumbnail. 081 while (displayImage == lastImage || displayImage == null || displayImage.getWidth() < 320) { 082 Thread.sleep(100); 083 } 084 } 085 while (paused) { 086 Thread.sleep(100); 087 } 088 wait(interval); 089 while (paused) { 090 Thread.sleep(100); 091 } 092 lastImage = StreetsideMainDialog.getInstance().getStreetsideImageDisplay().getImage(); 093 if (goForward) { 094 data.selectNext(followSelected); 095 } else { 096 data.selectPrevious(followSelected); 097 } 098 } catch (InterruptedException e) { 099 return; 100 } 101 } 102 } catch (NullPointerException e) { 103 if(StreetsideProperties.DEBUGING_ENABLED.get()) { 104 logger.debug(MessageFormat.format("Exception thrown in WalkThread: {0}", e.getMessage())); 105 e.printStackTrace(); 106 } 107 return; 108 } 109 end(); 110 } 111 112 private void preDownloadCubemaps(StreetsideImage startImage, int n) { 113 if (n >= 1 && startImage != null) { 114 115 for (int i = 0; i < 6; i++) { 116 for (int j = 0; j < 4; j++) { 117 for (int k = 0; k < 4; k++) { 118 119 CacheUtils.downloadPicture(startImage, CacheUtils.PICTURE.CUBEMAP); 120 if (startImage.next() instanceof StreetsideImage && n >= 2) { 121 preDownloadCubemaps((StreetsideImage) startImage.next(), n - 1); 122 } 123 } 124 } 125 } 126 } 127 } 128 129/** 130 * Downloads n images into the cache beginning from the supplied start-image (including the start-image itself). 131 * 132 * @param startImage the image to start with (this and the next n-1 images in the same sequence are downloaded) 133 * @param n the number of images to download 134 * @param type the quality of the image (full or thumbnail) 135 */ 136 private static void preDownloadImages(StreetsideImage startImage, int n, CacheUtils.PICTURE type) { 137 if (n >= 1 && startImage != null) { 138 CacheUtils.downloadPicture(startImage, type); 139 if (startImage.next() instanceof StreetsideImage && n >= 2) { 140 preDownloadImages((StreetsideImage) startImage.next(), n - 1, type); 141 } 142 } 143 } 144 145 @Override 146 public void imagesAdded() { 147 // Nothing 148 } 149 150 @Override 151 public void selectedImageChanged(StreetsideAbstractImage oldImage, StreetsideAbstractImage newImage) { 152 if (newImage != oldImage.next()) { 153 end(); 154 interrupt(); 155 } 156 } 157 158 /** 159 * Continues with the execution if paused. 160 */ 161 public void play() { 162 paused = false; 163 } 164 165 /** 166 * Pauses the execution. 167 */ 168 public void pause() { 169 paused = true; 170 } 171 172 /** 173 * Stops the execution. 174 */ 175 public void stopWalk() { 176 if (SwingUtilities.isEventDispatchThread()) { 177 end(); 178 interrupt(); 179 } else { 180 SwingUtilities.invokeLater(this::stopWalk); 181 } 182 } 183 184 /** 185 * Called when the walk stops by itself of forcefully. 186 */ 187 public void end() { 188 if (SwingUtilities.isEventDispatchThread()) { 189 end = true; 190 data.removeListener(this); 191 StreetsideMainDialog.getInstance().setMode(StreetsideMainDialog.MODE.NORMAL); 192 } else { 193 SwingUtilities.invokeLater(this::end); 194 } 195 } 196}