source: josm/trunk/src/org/openstreetmap/josm/data/imagery/GeorefImage.java@ 7425

Last change on this file since 7425 was 7425, checked in by Don-vip, 10 years ago

fix #3916 - WMS: Improve exception handling. Proper message is now displayed on tiles.

  • Property svn:eol-style set to native
File size: 8.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Font;
8import java.awt.Graphics;
9import java.awt.Image;
10import java.awt.Transparency;
11import java.awt.image.BufferedImage;
12import java.io.IOException;
13import java.io.ObjectInputStream;
14import java.io.ObjectOutputStream;
15import java.io.Serializable;
16import java.lang.ref.SoftReference;
17
18import javax.imageio.ImageIO;
19
20import org.openstreetmap.josm.data.coor.EastNorth;
21import org.openstreetmap.josm.gui.NavigatableComponent;
22import org.openstreetmap.josm.gui.layer.ImageryLayer;
23import org.openstreetmap.josm.gui.layer.WMSLayer;
24import org.openstreetmap.josm.tools.ImageProvider;
25
26public class GeorefImage implements Serializable {
27 private static final long serialVersionUID = 1L;
28
29 public enum State { IMAGE, NOT_IN_CACHE, FAILED, PARTLY_IN_CACHE}
30
31 private WMSLayer layer;
32 private State state;
33
34 private BufferedImage image;
35 private SoftReference<BufferedImage> reImg;
36 private int xIndex;
37 private int yIndex;
38
39 private static final Color transparentColor = new Color(0,0,0,0);
40 private Color fadeColor = transparentColor;
41
42 public EastNorth getMin() {
43 return layer.getEastNorth(xIndex, yIndex);
44 }
45
46 public EastNorth getMax() {
47 return layer.getEastNorth(xIndex+1, yIndex+1);
48 }
49
50 public GeorefImage(WMSLayer layer) {
51 this.layer = layer;
52 }
53
54 public void changePosition(int xIndex, int yIndex) {
55 if (!equalPosition(xIndex, yIndex)) {
56 this.xIndex = xIndex;
57 this.yIndex = yIndex;
58 this.image = null;
59 flushResizedCachedInstance();
60 }
61 }
62
63 public boolean equalPosition(int xIndex, int yIndex) {
64 return this.xIndex == xIndex && this.yIndex == yIndex;
65 }
66
67 /**
68 * Resets this image to initial state and release all resources being used.
69 * @since 7132
70 */
71 public void resetImage() {
72 if (image != null) {
73 image.flush();
74 }
75 changeImage(null, null, null);
76 }
77
78 public void changeImage(State state, BufferedImage image, String errorMsg) {
79 flushResizedCachedInstance();
80 this.image = image;
81 this.state = state;
82 if (state == null)
83 return;
84 switch (state) {
85 case FAILED:
86 BufferedImage imgFailed = createImage();
87 layer.drawErrorTile(imgFailed, errorMsg);
88 this.image = imgFailed;
89 break;
90 case NOT_IN_CACHE:
91 BufferedImage img = createImage();
92 Graphics g = img.getGraphics();
93 g.setColor(Color.GRAY);
94 g.fillRect(0, 0, img.getWidth(), img.getHeight());
95 Font font = g.getFont();
96 Font tempFont = font.deriveFont(Font.PLAIN).deriveFont(36.0f);
97 g.setFont(tempFont);
98 g.setColor(Color.BLACK);
99 String text = tr("Not in cache");
100 g.drawString(text, (img.getWidth() - g.getFontMetrics().stringWidth(text)) / 2, img.getHeight()/2);
101 g.setFont(font);
102 this.image = img;
103 break;
104 default:
105 if (this.image != null) {
106 this.image = layer.sharpenImage(this.image);
107 }
108 break;
109 }
110 }
111
112 private BufferedImage createImage() {
113 return new BufferedImage(layer.getImageSize(), layer.getImageSize(), BufferedImage.TYPE_INT_RGB);
114 }
115
116 public boolean paint(Graphics g, NavigatableComponent nc, int xIndex, int yIndex, int leftEdge, int bottomEdge) {
117 if (getImage() == null)
118 return false;
119
120 if(!(this.xIndex == xIndex && this.yIndex == yIndex))
121 return false;
122
123 int left = layer.getImageX(xIndex);
124 int bottom = layer.getImageY(yIndex);
125 int width = layer.getImageWidth(xIndex);
126 int height = layer.getImageHeight(yIndex);
127
128 int x = left - leftEdge;
129 int y = nc.getHeight() - (bottom - bottomEdge) - height;
130
131 // This happens if you zoom outside the world
132 if(width == 0 || height == 0)
133 return false;
134
135 // TODO: implement per-layer fade color
136 Color newFadeColor;
137 if (ImageryLayer.PROP_FADE_AMOUNT.get() == 0) {
138 newFadeColor = transparentColor;
139 } else {
140 newFadeColor = ImageryLayer.getFadeColorWithAlpha();
141 }
142
143 BufferedImage img = reImg == null?null:reImg.get();
144 if(img != null && img.getWidth() == width && img.getHeight() == height && fadeColor.equals(newFadeColor)) {
145 g.drawImage(img, x, y, null);
146 return true;
147 }
148
149 fadeColor = newFadeColor;
150
151 boolean alphaChannel = WMSLayer.PROP_ALPHA_CHANNEL.get() && getImage().getTransparency() != Transparency.OPAQUE;
152
153 try {
154 if(img != null) {
155 img.flush();
156 }
157 long freeMem = Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory();
158 // Notice that this value can get negative due to integer overflows
159
160 int multipl = alphaChannel ? 4 : 3;
161 // This happens when requesting images while zoomed out and then zooming in
162 // Storing images this large in memory will certainly hang up JOSM. Luckily
163 // traditional rendering is as fast at these zoom levels, so it's no loss.
164 // Also prevent caching if we're out of memory soon
165 if(width > 2000 || height > 2000 || width*height*multipl > freeMem) {
166 fallbackDraw(g, getImage(), x, y, width, height, alphaChannel);
167 } else {
168 // We haven't got a saved resized copy, so resize and cache it
169 img = new BufferedImage(width, height, alphaChannel?BufferedImage.TYPE_INT_ARGB:BufferedImage.TYPE_3BYTE_BGR);
170 img.getGraphics().drawImage(getImage(),
171 0, 0, width, height, // dest
172 0, 0, getImage().getWidth(null), getImage().getHeight(null), // src
173 null);
174 if (!alphaChannel) {
175 drawFadeRect(img.getGraphics(), 0, 0, width, height);
176 }
177 img.getGraphics().dispose();
178 g.drawImage(img, x, y, null);
179 reImg = new SoftReference<>(img);
180 }
181 } catch(Exception e) {
182 fallbackDraw(g, getImage(), x, y, width, height, alphaChannel);
183 }
184 return true;
185 }
186
187 private void fallbackDraw(Graphics g, Image img, int x, int y, int width, int height, boolean alphaChannel) {
188 flushResizedCachedInstance();
189 g.drawImage(
190 img, x, y, x + width, y + height,
191 0, 0, img.getWidth(null), img.getHeight(null),
192 null);
193 if (!alphaChannel) { //FIXME: fading for layers with alpha channel currently is not supported
194 drawFadeRect(g, x, y, width, height);
195 }
196 }
197
198 private void drawFadeRect(Graphics g, int x, int y, int width, int height) {
199 if (fadeColor != transparentColor) {
200 g.setColor(fadeColor);
201 g.fillRect(x, y, width, height);
202 }
203 }
204
205 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
206 state = (State) in.readObject();
207 boolean hasImage = in.readBoolean();
208 if (hasImage) {
209 image = ImageProvider.read(ImageIO.createImageInputStream(in), true, WMSLayer.PROP_ALPHA_CHANNEL.get());
210 } else {
211 in.readObject(); // read null from input stream
212 image = null;
213 }
214 }
215
216 private void writeObject(ObjectOutputStream out) throws IOException {
217 out.writeObject(state);
218 if(getImage() == null) {
219 out.writeBoolean(false);
220 out.writeObject(null);
221 } else {
222 out.writeBoolean(true);
223 ImageIO.write(getImage(), "png", ImageIO.createImageOutputStream(out));
224 }
225 }
226
227 public void flushResizedCachedInstance() {
228 if (reImg != null) {
229 BufferedImage img = reImg.get();
230 if (img != null) {
231 img.flush();
232 }
233 }
234 reImg = null;
235 }
236
237 public BufferedImage getImage() {
238 return image;
239 }
240
241 public State getState() {
242 return state;
243 }
244
245 public int getXIndex() {
246 return xIndex;
247 }
248
249 public int getYIndex() {
250 return yIndex;
251 }
252
253 public void setLayer(WMSLayer layer) {
254 this.layer = layer;
255 }
256}
Note: See TracBrowser for help on using the repository browser.