    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}