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}