001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.geoimage.viewers.projections;
003
004import java.awt.Component;
005import java.awt.Graphics;
006import java.awt.Image;
007import java.awt.Point;
008import java.awt.Rectangle;
009import java.awt.event.ComponentAdapter;
010import java.awt.event.ComponentEvent;
011import java.awt.image.BufferedImage;
012import java.util.Collections;
013import java.util.Set;
014
015import org.openstreetmap.josm.data.imagery.street_level.Projections;
016import org.openstreetmap.josm.gui.layer.geoimage.ImageDisplay;
017import org.openstreetmap.josm.gui.util.GuiHelper;
018import org.openstreetmap.josm.gui.util.imagery.CameraPlane;
019import org.openstreetmap.josm.gui.util.imagery.Vector3D;
020
021/**
022 * A class for showing 360 images that use the equirectangular projection
023 * @author Taylor Smock
024 * @since 18246
025 */
026public class Equirectangular extends ComponentAdapter implements IImageViewer {
027    private volatile CameraPlane cameraPlane;
028    private volatile BufferedImage offscreenImage;
029
030    @Override
031    public Set<Projections> getSupportedProjections() {
032        return Collections.singleton(Projections.EQUIRECTANGULAR);
033    }
034
035    @Override
036    public void paintImage(Graphics g, BufferedImage image, Rectangle target, Rectangle visibleRect) {
037        final CameraPlane currentCameraPlane;
038        final BufferedImage currentOffscreenImage;
039        synchronized (this) {
040            currentCameraPlane = this.cameraPlane;
041            currentOffscreenImage = this.offscreenImage;
042        }
043        currentCameraPlane.mapping(image, currentOffscreenImage);
044        if (target == null) {
045            target = new Rectangle(0, 0, currentOffscreenImage.getWidth(null), currentOffscreenImage.getHeight(null));
046        }
047        g.drawImage(currentOffscreenImage, target.x, target.y, target.x + target.width, target.y + target.height,
048                visibleRect.x, visibleRect.y, visibleRect.x + visibleRect.width, visibleRect.y + visibleRect.height,
049                null);
050    }
051
052    @Override
053    public ImageDisplay.VisRect getDefaultVisibleRectangle(Component component, Image image) {
054        return new ImageDisplay.VisRect(0, 0, component.getSize().width, component.getSize().height);
055    }
056
057    @Override
058    public Vector3D getRotation() {
059        return this.cameraPlane.getRotation();
060    }
061
062    @Override
063    public void componentResized(ComponentEvent e) {
064        final Component imgDisplay = e.getComponent();
065        if (e.getComponent().getWidth() > 0
066                && e.getComponent().getHeight() > 0) {
067            // FIXME: Do something so that the types of the images are the same between the offscreenImage and
068            // the image entry
069            final CameraPlane currentCameraPlane;
070            synchronized (this) {
071                currentCameraPlane = this.cameraPlane;
072            }
073            final BufferedImage temporaryOffscreenImage = new BufferedImage(imgDisplay.getWidth(), imgDisplay.getHeight(),
074                    BufferedImage.TYPE_4BYTE_ABGR);
075
076            Vector3D currentRotation = null;
077            if (currentCameraPlane != null) {
078                currentRotation = currentCameraPlane.getRotation();
079            }
080            final CameraPlane temporaryCameraPlane = new CameraPlane(imgDisplay.getWidth(), imgDisplay.getHeight());
081            if (currentRotation != null) {
082                temporaryCameraPlane.setRotation(currentRotation);
083            }
084            synchronized (this) {
085                this.cameraPlane = temporaryCameraPlane;
086                this.offscreenImage = temporaryOffscreenImage;
087            }
088            if (imgDisplay instanceof ImageDisplay) {
089                ((ImageDisplay) imgDisplay).updateVisibleRectangle();
090            }
091            GuiHelper.runInEDT(imgDisplay::revalidate);
092        }
093    }
094
095    @Override
096    public void mouseDragged(final Point from, final Point to, ImageDisplay.VisRect currentVisibleRect) {
097        if (from != null && to != null) {
098            this.cameraPlane.setRotationFromDelta(from, to);
099        }
100    }
101
102    @Override
103    public void checkAndModifyVisibleRectSize(Image image, ImageDisplay.VisRect visibleRect) {
104        IImageViewer.super.checkAndModifyVisibleRectSize(this.offscreenImage, visibleRect);
105    }
106
107    @Override
108    public Image getMaxImageSize(ImageDisplay imageDisplay, Image image) {
109        return this.offscreenImage;
110    }
111}