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