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.Map;
009
010import javax.swing.AbstractAction;
011import javax.swing.Action;
012import javax.swing.ImageIcon;
013
014import com.kitfox.svg.SVGDiagram;
015
016/**
017 * Holds data for one particular image.
018 * It can be backed by a svg or raster image.
019 *
020 * In the first case, 'svg' is not null and in the latter case, 'imgCache' has
021 * at least one entry for the key DEFAULT_DIMENSION.
022 * @since 4271
023 */
024public class ImageResource {
025
026    /**
027     * Caches the image data for resized versions of the same image.
028     */
029    private Map<Dimension, Image> imgCache = new HashMap<>();
030    private SVGDiagram svg;
031    /**
032     * Use this dimension to request original file dimension.
033     */
034    public static final Dimension DEFAULT_DIMENSION = new Dimension(-1, -1);
035
036    /**
037     * Constructs a new {@code ImageResource} from an image.
038     * @param img the image
039     */
040    public ImageResource(Image img) {
041        CheckParameterUtil.ensureParameterNotNull(img);
042        imgCache.put(DEFAULT_DIMENSION, img);
043    }
044
045    /**
046     * Constructs a new {@code ImageResource} from SVG data.
047     * @param svg SVG data
048     */
049    public ImageResource(SVGDiagram svg) {
050        CheckParameterUtil.ensureParameterNotNull(svg);
051        this.svg = svg;
052    }
053
054    /**
055     * Returns the image icon at default dimension.
056     * @return the image icon at default dimension
057     */
058    public ImageIcon getImageIcon() {
059        return getImageIcon(DEFAULT_DIMENSION);
060    }
061
062    /**
063     * Set both icons of an Action
064     * @param a The action for the icons
065     * @since 7693
066     */
067    public void getImageIcon(AbstractAction a) {
068        ImageIcon icon = getImageIconBounded(ImageProvider.getImageSizes(ImageProvider.ImageSizes.SMALLICON));
069        a.putValue(Action.SMALL_ICON, icon);
070        icon = getImageIconBounded(ImageProvider.getImageSizes(ImageProvider.ImageSizes.LARGEICON));
071        a.putValue(Action.LARGE_ICON_KEY, icon);
072    }
073
074    /**
075     * Get an ImageIcon object for the image of this resource
076     * @param   dim The requested dimensions. Use (-1,-1) for the original size
077     *          and (width, -1) to set the width, but otherwise scale the image
078     *          proportionally.
079     * @return ImageIcon object for the image of this resource, scaled according to dim
080     */
081    public ImageIcon getImageIcon(Dimension dim) {
082        if (dim.width < -1 || dim.width == 0 || dim.height < -1 || dim.height == 0)
083            throw new IllegalArgumentException(dim+" is invalid");
084        Image img = imgCache.get(dim);
085        if (img != null) {
086            return new ImageIcon(img);
087        }
088        if (svg != null) {
089            img = ImageProvider.createImageFromSvg(svg, dim);
090            if (img == null) {
091                return null;
092            }
093            imgCache.put(dim, img);
094            return new ImageIcon(img);
095        } else {
096            Image base = imgCache.get(DEFAULT_DIMENSION);
097            if (base == null) throw new AssertionError();
098
099            int width = dim.width;
100            int height = dim.height;
101            ImageIcon icon = new ImageIcon(base);
102            if (width == -1) {
103                width = Math.max(1, icon.getIconWidth() * height / icon.getIconHeight());
104            } else if (height == -1) {
105                height = Math.max(1, icon.getIconHeight() * width / icon.getIconWidth());
106            }
107            Image i = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
108            img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
109            img.getGraphics().drawImage(i, 0, 0, null);
110            imgCache.put(dim, img);
111            return new ImageIcon(img);
112        }
113    }
114
115    /**
116     * Get image icon with a certain maximum size. The image is scaled down
117     * to fit maximum dimensions. (Keeps aspect ratio)
118     *
119     * @param maxSize The maximum size. One of the dimensions (widht or height) can be -1,
120     * which means it is not bounded.
121     * @return ImageIcon object for the image of this resource, scaled down if needed, according to maxSize
122     */
123    public ImageIcon getImageIconBounded(Dimension maxSize) {
124        if (maxSize.width < -1 || maxSize.width == 0 || maxSize.height < -1 || maxSize.height == 0)
125            throw new IllegalArgumentException(maxSize+" is invalid");
126        float realWidth;
127        float realHeight;
128        if (svg != null) {
129            realWidth = svg.getWidth();
130            realHeight = svg.getHeight();
131        } else {
132            Image base = imgCache.get(DEFAULT_DIMENSION);
133            if (base == null) throw new AssertionError();
134            ImageIcon icon = new ImageIcon(base);
135            realWidth = icon.getIconWidth();
136            realHeight = icon.getIconHeight();
137        }
138        int maxWidth = maxSize.width;
139        int maxHeight = maxSize.height;
140
141        if (realWidth <= maxWidth) {
142            maxWidth = -1;
143        }
144        if (realHeight <= maxHeight) {
145            maxHeight = -1;
146        }
147
148        if (maxWidth == -1 && maxHeight == -1)
149            return getImageIcon(DEFAULT_DIMENSION);
150        else if (maxWidth == -1)
151            return getImageIcon(new Dimension(-1, maxHeight));
152        else if (maxHeight == -1)
153            return getImageIcon(new Dimension(maxWidth, -1));
154        else
155            if (realWidth / maxWidth > realHeight / maxHeight)
156                return getImageIcon(new Dimension(maxWidth, -1));
157            else
158                return getImageIcon(new Dimension(-1, maxHeight));
159   }
160}