source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/MapImage.java@ 13250

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

fix #15654 - repair asynchronous fetching of icons (regression from r10714) + fetch https icons asynchronously (omission from r10731)

  • Property svn:eol-style set to native
File size: 9.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.styleelement;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Graphics;
7import java.awt.Image;
8import java.awt.Rectangle;
9import java.awt.image.BufferedImage;
10import java.util.Objects;
11
12import javax.swing.ImageIcon;
13
14import org.openstreetmap.josm.gui.MainApplication;
15import org.openstreetmap.josm.gui.MapView;
16import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
17import org.openstreetmap.josm.gui.mappaint.StyleSource;
18import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.BoxProvider;
19import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement.BoxProviderResult;
20import org.openstreetmap.josm.gui.util.GuiHelper;
21import org.openstreetmap.josm.tools.ImageProvider;
22import org.openstreetmap.josm.tools.Utils;
23
24/**
25 * An image that will be displayed on the map.
26 */
27public class MapImage {
28
29 private static final int MAX_SIZE = 48;
30
31 /**
32 * ImageIcon can change while the image is loading.
33 */
34 private Image img;
35
36 /**
37 * The alpha (opacity) value of the image. It is multiplied to the image alpha channel.
38 * Range: 0...255
39 */
40 public int alpha = 255;
41 /**
42 * The name of the image that should be displayed. It is given to the {@link ImageProvider}
43 */
44 public String name;
45 /**
46 * The StyleSource that registered the image
47 */
48 public StyleSource source;
49 /**
50 * A flag indicating that the image should automatically be scaled to the right size.
51 */
52 public boolean autoRescale;
53 /**
54 * The width of the image, as set by MapCSS
55 */
56 public int width = -1;
57 /**
58 * The height of the image, as set by MapCSS
59 */
60 public int height = -1;
61 /**
62 * The x offset of the anchor of this image
63 */
64 public int offsetX;
65 /**
66 * The y offset of the anchor of this image
67 */
68 public int offsetY;
69
70 private boolean temporary;
71
72 /**
73 * A cache that holds a disabled (gray) version of this image
74 */
75 private BufferedImage disabledImgCache;
76
77 /**
78 * Creates a new {@link MapImage}
79 * @param name The image name
80 * @param source The style source that requests this image
81 */
82 public MapImage(String name, StyleSource source) {
83 this(name, source, true);
84 }
85
86 /**
87 * Creates a new {@link MapImage}
88 * @param name The image name
89 * @param source The style source that requests this image
90 * @param autoRescale A flag indicating to automatically adjust the width/height of the image
91 */
92 public MapImage(String name, StyleSource source, boolean autoRescale) {
93 this.name = name;
94 this.source = source;
95 this.autoRescale = autoRescale;
96 }
97
98 /**
99 * Get the image associated with this MapImage object.
100 *
101 * @param disabled {@code} true to request disabled version, {@code false} for the standard version
102 * @return the image
103 */
104 public Image getImage(boolean disabled) {
105 if (disabled) {
106 return getDisabled();
107 } else {
108 return getImage();
109 }
110 }
111
112 private Image getDisabled() {
113 if (disabledImgCache != null)
114 return disabledImgCache;
115 if (img == null)
116 getImage(); // fix #7498 ?
117 Image disImg = GuiHelper.getDisabledImage(img);
118 if (disImg instanceof BufferedImage) {
119 disabledImgCache = (BufferedImage) disImg;
120 } else {
121 disabledImgCache = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
122 Graphics g = disabledImgCache.getGraphics();
123 g.drawImage(disImg, 0, 0, null);
124 g.dispose();
125 }
126 return disabledImgCache;
127 }
128
129 private Image getImage() {
130 if (img != null)
131 return img;
132 temporary = false;
133 new ImageProvider(name)
134 .setDirs(MapPaintStyles.getIconSourceDirs(source))
135 .setId("mappaint."+source.getPrefName())
136 .setArchive(source.zipIcons)
137 .setInArchiveDir(source.getZipEntryDirName())
138 .setWidth(width)
139 .setHeight(height)
140 .setOptional(true)
141 .getAsync().thenAcceptAsync(result -> {
142 synchronized (this) {
143 if (result == null) {
144 source.logWarning(tr("Failed to locate image ''{0}''", name));
145 ImageIcon noIcon = MapPaintStyles.getNoIconIcon(source);
146 img = noIcon == null ? null : noIcon.getImage();
147 } else {
148 img = rescale(result.getImage());
149 }
150 if (temporary) {
151 disabledImgCache = null;
152 MapView mapView = MainApplication.getMap().mapView;
153 mapView.preferenceChanged(null); // otherwise repaint is ignored, because layer hasn't changed
154 mapView.repaint();
155 }
156 temporary = false;
157 }
158 }
159 );
160 synchronized (this) {
161 if (img == null) {
162 img = ImageProvider.get("clock").getImage();
163 temporary = true;
164 }
165 }
166 return img;
167 }
168
169 /**
170 * Gets the image width
171 * @return The real image width
172 */
173 public int getWidth() {
174 return getImage().getWidth(null);
175 }
176
177 /**
178 * Gets the image height
179 * @return The real image height
180 */
181 public int getHeight() {
182 return getImage().getHeight(null);
183 }
184
185 /**
186 * Gets the alpha value the image should be multiplied with
187 * @return The value in range 0..1
188 */
189 public float getAlphaFloat() {
190 return Utils.colorInt2float(alpha);
191 }
192
193 /**
194 * Determines if image is not completely loaded and {@code getImage()} returns a temporary image.
195 * @return {@code true} if image is not completely loaded and getImage() returns a temporary image
196 */
197 public boolean isTemporary() {
198 return temporary;
199 }
200
201 protected class MapImageBoxProvider implements BoxProvider {
202 @Override
203 public BoxProviderResult get() {
204 return new BoxProviderResult(box(), temporary);
205 }
206
207 private Rectangle box() {
208 int w = getWidth(), h = getHeight();
209 if (mustRescale(getImage())) {
210 w = 16;
211 h = 16;
212 }
213 return new Rectangle(-w/2, -h/2, w, h);
214 }
215
216 private MapImage getParent() {
217 return MapImage.this;
218 }
219
220 @Override
221 public int hashCode() {
222 return MapImage.this.hashCode();
223 }
224
225 @Override
226 public boolean equals(Object obj) {
227 if (!(obj instanceof BoxProvider))
228 return false;
229 if (obj instanceof MapImageBoxProvider) {
230 MapImageBoxProvider other = (MapImageBoxProvider) obj;
231 return MapImage.this.equals(other.getParent());
232 } else if (temporary) {
233 return false;
234 } else {
235 final BoxProvider other = (BoxProvider) obj;
236 BoxProviderResult resultOther = other.get();
237 if (resultOther.isTemporary()) return false;
238 return box().equals(resultOther.getBox());
239 }
240 }
241 }
242
243 /**
244 * Gets a box provider that provides a box that covers the size of this image
245 * @return The box provider
246 */
247 public BoxProvider getBoxProvider() {
248 return new MapImageBoxProvider();
249 }
250
251 /**
252 * Rescale excessively large images.
253 * @param image the unscaled image
254 * @return The scaled down version to 16x16 pixels if the image height and width exceeds 48 pixels and no size has been explicitely specified
255 */
256 private Image rescale(Image image) {
257 if (image == null) return null;
258 // Scale down large (.svg) images to 16x16 pixels if no size is explicitely specified
259 if (mustRescale(image)) {
260 return ImageProvider.createBoundedImage(image, 16);
261 } else {
262 return image;
263 }
264 }
265
266 private boolean mustRescale(Image image) {
267 return autoRescale && width == -1 && image.getWidth(null) > MAX_SIZE
268 && height == -1 && image.getHeight(null) > MAX_SIZE;
269 }
270
271 @Override
272 public boolean equals(Object obj) {
273 if (this == obj) return true;
274 if (obj == null || getClass() != obj.getClass()) return false;
275 MapImage mapImage = (MapImage) obj;
276 return alpha == mapImage.alpha &&
277 autoRescale == mapImage.autoRescale &&
278 width == mapImage.width &&
279 height == mapImage.height &&
280 Objects.equals(name, mapImage.name) &&
281 Objects.equals(source, mapImage.source);
282 }
283
284 @Override
285 public int hashCode() {
286 return Objects.hash(alpha, name, source, autoRescale, width, height);
287 }
288
289 @Override
290 public String toString() {
291 return name;
292 }
293}
Note: See TracBrowser for help on using the repository browser.