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