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