001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.mappaint;
003    
004    import static org.openstreetmap.josm.tools.Utils.equal;
005    
006    import java.awt.Image;
007    import java.awt.Rectangle;
008    import java.awt.image.BufferedImage;
009    
010    import javax.swing.ImageIcon;
011    
012    import org.openstreetmap.josm.Main;
013    import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProvider;
014    import org.openstreetmap.josm.gui.mappaint.BoxTextElemStyle.BoxProviderResult;
015    import org.openstreetmap.josm.gui.util.GuiHelper;
016    import org.openstreetmap.josm.tools.ImageProvider;
017    import org.openstreetmap.josm.tools.ImageProvider.ImageCallback;
018    import org.openstreetmap.josm.tools.Utils;
019    
020    public class MapImage {
021        /**
022         * ImageIcon can change while the image is loading.
023         */
024        private BufferedImage img;
025    
026        /**
027         * The 5 following fields are only used to check for equality.
028         */
029        public int alpha = 255;
030        public String name;
031        public StyleSource source;
032        public int width = -1;
033        public int height = -1;
034    
035        private boolean temporary;
036        private Image disabledImg;
037    
038        public MapImage(String name, StyleSource source) {
039            this.name = name;
040            this.source = source;
041        }
042    
043        public Image getDisabled() {
044            if (disabledImg != null)
045                return disabledImg;
046            if (img == null)
047                getImage(); // fix #7498 ?
048            disabledImg = GuiHelper.getDisabledImage(img);
049            return disabledImg;
050        }
051    
052        public BufferedImage getImage() {
053            if (img != null)
054                return img;
055            temporary = false;
056            new ImageProvider(name)
057                    .setDirs(MapPaintStyles.getIconSourceDirs(source))
058                    .setId("mappaint."+source.getPrefName())
059                    .setArchive(source.zipIcons)
060                    .setWidth(width)
061                    .setHeight(height)
062                    .setOptional(true)
063                    .getInBackground(new ImageCallback() {
064                        @Override
065                        public void finished(ImageIcon result) {
066                            synchronized (MapImage.this) {
067                                if (result == null) {
068                                    ImageIcon noIcon = MapPaintStyles.getNoIcon_Icon(source);
069                                    img = noIcon == null ? null : (BufferedImage) noIcon.getImage();
070                                } else {
071                                    img = (BufferedImage) result.getImage();
072                                }
073                                if (temporary) {
074                                    Main.map.mapView.preferenceChanged(null); // otherwise repaint is ignored, because layer hasn't changed
075                                    Main.map.mapView.repaint();
076                                }
077                                temporary = false;
078                            }
079                        }
080                    }
081            );
082            synchronized (this) {
083                if (img == null) {
084                    img = (BufferedImage) ImageProvider.get("clock").getImage();
085                    temporary = true;
086                }
087            }
088            return img;
089        }
090    
091        public int getWidth() {
092            return getImage().getWidth(null);
093        }
094    
095        public int getHeight() {
096            return getImage().getHeight(null);
097        }
098    
099        public float getAlphaFloat() {
100            return Utils.color_int2float(alpha);
101        }
102    
103        /**
104         * Returns true, if image is not completely loaded and getImage() returns a temporary image.
105         */
106        public boolean isTemporary() {
107            return temporary;
108        }
109    
110        protected class MapImageBoxProvider implements BoxProvider {
111            @Override
112            public BoxProviderResult get() {
113                return new BoxProviderResult(box(), temporary);
114            }
115    
116            private Rectangle box() {
117                int w = getWidth(), h = getHeight();
118                return new Rectangle(-w/2, -h/2, w, h);
119            }
120    
121            private MapImage getParent() {
122                return MapImage.this;
123            }
124    
125            @Override
126            public int hashCode() {
127                return MapImage.this.hashCode();
128            }
129    
130            @Override
131            public boolean equals(Object obj) {
132                if (obj == null || !(obj instanceof BoxProvider))
133                    return false;
134                if (obj instanceof MapImageBoxProvider) {
135                    MapImageBoxProvider other = (MapImageBoxProvider) obj;
136                    return MapImage.this.equals(other.getParent());
137                } else if (temporary) {
138                    return false;
139                } else {
140                    final BoxProvider other = (BoxProvider) obj;
141                    BoxProviderResult resultOther = other.get();
142                    if (resultOther.isTemporary()) return false;
143                    return box().equals(resultOther.getBox());
144                }
145            }
146        }
147    
148        public BoxProvider getBoxProvider() {
149            return new MapImageBoxProvider();
150        }
151    
152        @Override
153        public boolean equals(Object obj) {
154            if (obj == null || getClass() != obj.getClass())
155                return false;
156            final MapImage other = (MapImage) obj;
157            // img changes when image is fully loaded and can't be used for equality check.
158            return  alpha == other.alpha &&
159                    equal(name, other.name) &&
160                    equal(source, other.source) &&
161                    width == other.width &&
162                    height == other.height;
163        }
164    
165        @Override
166        public int hashCode() {
167            int hash = 7;
168            hash = 67 * hash + alpha;
169            hash = 67 * hash + name.hashCode();
170            hash = 67 * hash + source.hashCode();
171            hash = 67 * hash + width;
172            hash = 67 * hash + height;
173            return hash;
174        }
175    
176        @Override
177        public String toString() {
178            return name;
179        }
180    }