1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.gui.layer.geoimage.viewers.projections;
|
---|
3 |
|
---|
4 | import java.awt.Component;
|
---|
5 | import java.awt.Graphics;
|
---|
6 | import java.awt.Image;
|
---|
7 | import java.awt.Point;
|
---|
8 | import java.awt.Rectangle;
|
---|
9 | import java.awt.event.ComponentAdapter;
|
---|
10 | import java.awt.event.ComponentEvent;
|
---|
11 | import java.awt.image.BufferedImage;
|
---|
12 | import java.util.Collections;
|
---|
13 | import java.util.Set;
|
---|
14 |
|
---|
15 | import org.openstreetmap.josm.data.imagery.street_level.Projections;
|
---|
16 | import org.openstreetmap.josm.gui.layer.geoimage.ImageDisplay;
|
---|
17 | import org.openstreetmap.josm.gui.util.GuiHelper;
|
---|
18 | import org.openstreetmap.josm.gui.util.imagery.CameraPlane;
|
---|
19 | import org.openstreetmap.josm.gui.util.imagery.Vector3D;
|
---|
20 |
|
---|
21 | import jakarta.annotation.Nonnull;
|
---|
22 | import jakarta.annotation.Nullable;
|
---|
23 |
|
---|
24 | /**
|
---|
25 | * A class for showing 360 images that use the equirectangular projection
|
---|
26 | * @author Taylor Smock
|
---|
27 | * @since 18246
|
---|
28 | */
|
---|
29 | public class Equirectangular extends ComponentAdapter implements IImageViewer {
|
---|
30 | @Nullable
|
---|
31 | private volatile CameraPlane cameraPlane;
|
---|
32 | private volatile BufferedImage offscreenImage;
|
---|
33 |
|
---|
34 | @Override
|
---|
35 | public Set<Projections> getSupportedProjections() {
|
---|
36 | return Collections.singleton(Projections.EQUIRECTANGULAR);
|
---|
37 | }
|
---|
38 |
|
---|
39 | @Override
|
---|
40 | public void paintImage(Graphics g, BufferedImage image, Rectangle target, Rectangle visibleRect) {
|
---|
41 | final CameraPlane currentCameraPlane;
|
---|
42 | final BufferedImage currentOffscreenImage;
|
---|
43 | synchronized (this) {
|
---|
44 | final CameraPlane currentPlane = this.cameraPlane;
|
---|
45 | currentCameraPlane = currentPlane != null ? currentPlane : this.updateCameraPlane(target.width, target.height);
|
---|
46 | currentOffscreenImage = this.offscreenImage;
|
---|
47 | }
|
---|
48 | currentCameraPlane.mapping(image, currentOffscreenImage, visibleRect);
|
---|
49 | if (target == null) {
|
---|
50 | target = new Rectangle(0, 0, currentOffscreenImage.getWidth(null), currentOffscreenImage.getHeight(null));
|
---|
51 | }
|
---|
52 | g.drawImage(currentOffscreenImage, target.x, target.y, target.x + target.width, target.y + target.height,
|
---|
53 | visibleRect.x, visibleRect.y, visibleRect.x + visibleRect.width, visibleRect.y + visibleRect.height,
|
---|
54 | null);
|
---|
55 | }
|
---|
56 |
|
---|
57 | @Override
|
---|
58 | public ImageDisplay.VisRect getDefaultVisibleRectangle(Component component, Image image) {
|
---|
59 | return new ImageDisplay.VisRect(0, 0, component.getSize().width, component.getSize().height);
|
---|
60 | }
|
---|
61 |
|
---|
62 | @Override
|
---|
63 | public Vector3D getRotation() {
|
---|
64 | final CameraPlane currentPlane = this.cameraPlane;
|
---|
65 | if (currentPlane != null) {
|
---|
66 | return currentPlane.getRotation();
|
---|
67 | }
|
---|
68 | return IImageViewer.super.getRotation();
|
---|
69 | }
|
---|
70 |
|
---|
71 | @Override
|
---|
72 | public void componentResized(ComponentEvent e) {
|
---|
73 | final Component imgDisplay = e.getComponent();
|
---|
74 | if (e.getComponent().getWidth() > 0
|
---|
75 | && e.getComponent().getHeight() > 0) {
|
---|
76 | updateCameraPlane(imgDisplay.getWidth(), imgDisplay.getHeight());
|
---|
77 | if (imgDisplay instanceof ImageDisplay) {
|
---|
78 | ((ImageDisplay) imgDisplay).updateVisibleRectangle();
|
---|
79 | }
|
---|
80 | GuiHelper.runInEDT(imgDisplay::revalidate);
|
---|
81 | }
|
---|
82 | }
|
---|
83 |
|
---|
84 | /**
|
---|
85 | * Update the current camera plane
|
---|
86 | * @param width The width to use
|
---|
87 | * @param height The height to use
|
---|
88 | */
|
---|
89 | @Nonnull
|
---|
90 | private CameraPlane updateCameraPlane(int width, int height) {
|
---|
91 | // FIXME: Do something so that the types of the images are the same between the offscreenImage and
|
---|
92 | // the image entry
|
---|
93 | final CameraPlane currentCameraPlane;
|
---|
94 | synchronized (this) {
|
---|
95 | currentCameraPlane = this.cameraPlane;
|
---|
96 | }
|
---|
97 | final BufferedImage temporaryOffscreenImage = new BufferedImage(width, height,
|
---|
98 | BufferedImage.TYPE_4BYTE_ABGR);
|
---|
99 |
|
---|
100 | Vector3D currentRotation = null;
|
---|
101 | if (currentCameraPlane != null) {
|
---|
102 | currentRotation = currentCameraPlane.getRotation();
|
---|
103 | }
|
---|
104 | final CameraPlane temporaryCameraPlane = new CameraPlane(width, height);
|
---|
105 | if (currentRotation != null) {
|
---|
106 | temporaryCameraPlane.setRotation(currentRotation);
|
---|
107 | }
|
---|
108 | synchronized (this) {
|
---|
109 | this.cameraPlane = temporaryCameraPlane;
|
---|
110 | this.offscreenImage = temporaryOffscreenImage;
|
---|
111 | }
|
---|
112 | return temporaryCameraPlane;
|
---|
113 | }
|
---|
114 |
|
---|
115 | @Override
|
---|
116 | public void mouseDragged(final Point from, final Point to, ImageDisplay.VisRect currentVisibleRect) {
|
---|
117 | final CameraPlane currentPlane = this.cameraPlane;
|
---|
118 | if (from != null && to != null && currentPlane != null) {
|
---|
119 | currentPlane.setRotationFromDelta(from, to);
|
---|
120 | }
|
---|
121 | }
|
---|
122 |
|
---|
123 | @Override
|
---|
124 | public void checkAndModifyVisibleRectSize(Image image, ImageDisplay.VisRect visibleRect) {
|
---|
125 | IImageViewer.super.checkAndModifyVisibleRectSize(this.offscreenImage, visibleRect);
|
---|
126 | }
|
---|
127 |
|
---|
128 | @Override
|
---|
129 | public Image getMaxImageSize(ImageDisplay imageDisplay, Image image) {
|
---|
130 | return this.offscreenImage;
|
---|
131 | }
|
---|
132 | }
|
---|