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