001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.awt.Dimension; 005import java.awt.Image; 006import java.awt.image.BufferedImage; 007import java.util.HashMap; 008import java.util.List; 009import java.util.Map; 010 011import javax.swing.AbstractAction; 012import javax.swing.Action; 013import javax.swing.ImageIcon; 014 015import com.kitfox.svg.SVGDiagram; 016 017/** 018 * Holds data for one particular image. 019 * It can be backed by a svg or raster image. 020 * 021 * In the first case, <code>svg</code> is not <code>null</code> and in the latter case, 022 * <code>baseImage</code> is not <code>null</code>. 023 * @since 4271 024 */ 025public class ImageResource { 026 027 /** 028 * Caches the image data for resized versions of the same image. 029 */ 030 private Map<Dimension, Image> imgCache = new HashMap<>(); 031 /** 032 * SVG diagram information in case of SVG vector image. 033 */ 034 private SVGDiagram svg; 035 /** 036 * Use this dimension to request original file dimension. 037 */ 038 public static final Dimension DEFAULT_DIMENSION = new Dimension(-1, -1); 039 /** 040 * ordered list of overlay images 041 */ 042 protected List<ImageOverlay> overlayInfo = null; 043 private Image baseImage = null; 044 045 /** 046 * Constructs a new {@code ImageResource} from an image. 047 * @param img the image 048 */ 049 public ImageResource(Image img) { 050 CheckParameterUtil.ensureParameterNotNull(img); 051 this.baseImage = img; 052 imgCache.put(DEFAULT_DIMENSION, img); 053 } 054 055 /** 056 * Constructs a new {@code ImageResource} from SVG data. 057 * @param svg SVG data 058 */ 059 public ImageResource(SVGDiagram svg) { 060 CheckParameterUtil.ensureParameterNotNull(svg); 061 this.svg = svg; 062 } 063 064 /** 065 * Constructs a new {@code ImageResource} from another one and sets overlays. 066 * @param res the existing resource 067 * @param overlayInfo the overlay to apply 068 * @since 8095 069 */ 070 public ImageResource(ImageResource res, List<ImageOverlay> overlayInfo) { 071 this.svg = res.svg; 072 this.baseImage = res.baseImage; 073 this.overlayInfo = overlayInfo; 074 } 075 076 /** 077 * Returns the image icon at default dimension. 078 * @return the image icon at default dimension 079 */ 080 public ImageIcon getImageIcon() { 081 return getImageIcon(DEFAULT_DIMENSION); 082 } 083 084 /** 085 * Set both icons of an Action 086 * @param a The action for the icons 087 * @since 7693 088 */ 089 public void getImageIcon(AbstractAction a) { 090 ImageIcon icon = getImageIconBounded(ImageProvider.getImageSizes(ImageProvider.ImageSizes.SMALLICON)); 091 a.putValue(Action.SMALL_ICON, icon); 092 icon = getImageIconBounded(ImageProvider.getImageSizes(ImageProvider.ImageSizes.LARGEICON)); 093 a.putValue(Action.LARGE_ICON_KEY, icon); 094 } 095 096 /** 097 * Get an ImageIcon object for the image of this resource 098 * @param dim The requested dimensions. Use (-1,-1) for the original size 099 * and (width, -1) to set the width, but otherwise scale the image 100 * proportionally. 101 * @return ImageIcon object for the image of this resource, scaled according to dim 102 */ 103 public ImageIcon getImageIcon(Dimension dim) { 104 if (dim.width < -1 || dim.width == 0 || dim.height < -1 || dim.height == 0) 105 throw new IllegalArgumentException(dim+" is invalid"); 106 Image img = imgCache.get(dim); 107 if (img != null) { 108 return new ImageIcon(img); 109 } 110 if (svg != null) { 111 BufferedImage bimg = ImageProvider.createImageFromSvg(svg, dim); 112 if (bimg == null) { 113 return null; 114 } 115 if (overlayInfo != null) { 116 for (ImageOverlay o : overlayInfo) { 117 o.apply(bimg); 118 } 119 } 120 imgCache.put(dim, bimg); 121 return new ImageIcon(bimg); 122 } else { 123 if (baseImage == null) throw new AssertionError(); 124 125 int width = dim.width; 126 int height = dim.height; 127 ImageIcon icon = new ImageIcon(baseImage); 128 if (width == -1 && height == -1) { 129 width = icon.getIconWidth(); 130 height = icon.getIconHeight(); 131 } else if (width == -1) { 132 width = Math.max(1, icon.getIconWidth() * height / icon.getIconHeight()); 133 } else if (height == -1) { 134 height = Math.max(1, icon.getIconHeight() * width / icon.getIconWidth()); 135 } 136 Image i = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH); 137 BufferedImage bimg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 138 bimg.getGraphics().drawImage(i, 0, 0, null); 139 if (overlayInfo != null) { 140 for (ImageOverlay o : overlayInfo) { 141 o.apply(bimg); 142 } 143 } 144 imgCache.put(dim, bimg); 145 return new ImageIcon(bimg); 146 } 147 } 148 149 /** 150 * Get image icon with a certain maximum size. The image is scaled down 151 * to fit maximum dimensions. (Keeps aspect ratio) 152 * 153 * @param maxSize The maximum size. One of the dimensions (width or height) can be -1, 154 * which means it is not bounded. 155 * @return ImageIcon object for the image of this resource, scaled down if needed, according to maxSize 156 */ 157 public ImageIcon getImageIconBounded(Dimension maxSize) { 158 if (maxSize.width < -1 || maxSize.width == 0 || maxSize.height < -1 || maxSize.height == 0) 159 throw new IllegalArgumentException(maxSize+" is invalid"); 160 float realWidth; 161 float realHeight; 162 if (svg != null) { 163 realWidth = svg.getWidth(); 164 realHeight = svg.getHeight(); 165 } else { 166 if (baseImage == null) throw new AssertionError(); 167 ImageIcon icon = new ImageIcon(baseImage); 168 realWidth = icon.getIconWidth(); 169 realHeight = icon.getIconHeight(); 170 } 171 int maxWidth = maxSize.width; 172 int maxHeight = maxSize.height; 173 174 if (realWidth <= maxWidth) { 175 maxWidth = -1; 176 } 177 if (realHeight <= maxHeight) { 178 maxHeight = -1; 179 } 180 181 if (maxWidth == -1 && maxHeight == -1) 182 return getImageIcon(DEFAULT_DIMENSION); 183 else if (maxWidth == -1) 184 return getImageIcon(new Dimension(-1, maxHeight)); 185 else if (maxHeight == -1) 186 return getImageIcon(new Dimension(maxWidth, -1)); 187 else if (realWidth / maxWidth > realHeight / maxHeight) 188 return getImageIcon(new Dimension(maxWidth, -1)); 189 else 190 return getImageIcon(new Dimension(-1, maxHeight)); 191 } 192}