source: josm/trunk/src/org/openstreetmap/josm/tools/ImageResource.java@ 17369

Last change on this file since 17369 was 17369, checked in by simon04, 3 years ago

see #20141 - Revert "ImageProvider: cache rendered SVG images using JCS"

  • Property svn:eol-style set to native
File size: 9.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.awt.Dimension;
5import java.awt.Image;
6import java.awt.image.BufferedImage;
7import java.util.List;
8import java.util.Map;
9import java.util.concurrent.ConcurrentHashMap;
10
11import javax.swing.AbstractAction;
12import javax.swing.Action;
13import javax.swing.Icon;
14import javax.swing.ImageIcon;
15import javax.swing.JPanel;
16import javax.swing.UIManager;
17
18import com.kitfox.svg.SVGDiagram;
19
20/**
21 * Holds data for one particular image.
22 * It can be backed by a svg or raster image.
23 *
24 * In the first case, <code>svg</code> is not <code>null</code> and in the latter case,
25 * <code>baseImage</code> is not <code>null</code>.
26 * @since 4271
27 */
28public class ImageResource {
29
30 /**
31 * Caches the image data for resized versions of the same image. The key is obtained using {@link ImageResizeMode#cacheKey(Dimension)}.
32 */
33 private final Map<Integer, BufferedImage> imgCache = new ConcurrentHashMap<>(4);
34 /**
35 * SVG diagram information in case of SVG vector image.
36 */
37 private SVGDiagram svg;
38 /**
39 * Use this dimension to request original file dimension.
40 */
41 public static final Dimension DEFAULT_DIMENSION = new Dimension(-1, -1);
42 /**
43 * ordered list of overlay images
44 */
45 protected List<ImageOverlay> overlayInfo;
46 /**
47 * <code>true</code> if icon must be grayed out
48 */
49 protected boolean isDisabled;
50 /**
51 * The base raster image for the final output
52 */
53 private Image baseImage;
54
55 /**
56 * Constructs a new {@code ImageResource} from an image.
57 * @param img the image
58 */
59 public ImageResource(Image img) {
60 CheckParameterUtil.ensureParameterNotNull(img);
61 baseImage = img;
62 }
63
64 /**
65 * Constructs a new {@code ImageResource} from SVG data.
66 * @param svg SVG data
67 */
68 public ImageResource(SVGDiagram svg) {
69 CheckParameterUtil.ensureParameterNotNull(svg);
70 this.svg = svg;
71 }
72
73 /**
74 * Constructs a new {@code ImageResource} from another one and sets overlays.
75 * @param res the existing resource
76 * @param overlayInfo the overlay to apply
77 * @since 8095
78 */
79 public ImageResource(ImageResource res, List<ImageOverlay> overlayInfo) {
80 this.svg = res.svg;
81 this.baseImage = res.baseImage;
82 this.overlayInfo = overlayInfo;
83 }
84
85 /**
86 * Set, if image must be filtered to grayscale so it will look like disabled icon.
87 *
88 * @param disabled true, if image must be grayed out for disabled state
89 * @return the current object, for convenience
90 * @since 10428
91 */
92 public ImageResource setDisabled(boolean disabled) {
93 this.isDisabled = disabled;
94 return this;
95 }
96
97 /**
98 * Set both icons of an Action
99 * @param a The action for the icons
100 * @since 10369
101 */
102 public void attachImageIcon(AbstractAction a) {
103 Dimension iconDimension = ImageProvider.ImageSizes.SMALLICON.getImageDimension();
104 ImageIcon icon = getImageIcon(iconDimension);
105 a.putValue(Action.SMALL_ICON, icon);
106
107 iconDimension = ImageProvider.ImageSizes.LARGEICON.getImageDimension();
108 icon = getImageIcon(iconDimension);
109 a.putValue(Action.LARGE_ICON_KEY, icon);
110 }
111
112 /**
113 * Set both icons of an Action
114 * @param a The action for the icons
115 * @param attachImageResource Adds an resource named "ImageResource" if <code>true</code>
116 * @since 10369
117 */
118 public void attachImageIcon(AbstractAction a, boolean attachImageResource) {
119 attachImageIcon(a);
120 if (attachImageResource) {
121 a.putValue("ImageResource", this);
122 }
123 }
124
125 /**
126 * Returns the image icon at default dimension.
127 * @return the image icon at default dimension
128 */
129 public ImageIcon getImageIcon() {
130 return getImageIcon(DEFAULT_DIMENSION);
131 }
132
133 /**
134 * Get an ImageIcon object for the image of this resource.
135 * <p>
136 * Will return a multi-resolution image by default (if possible).
137 * @param dim The requested dimensions. Use (-1,-1) for the original size and (width, -1)
138 * to set the width, but otherwise scale the image proportionally.
139 * @return ImageIcon object for the image of this resource, scaled according to dim
140 * @see #getImageIconBounded(java.awt.Dimension)
141 */
142 public ImageIcon getImageIcon(Dimension dim) {
143 return getImageIcon(dim, true, null);
144 }
145
146 /**
147 * Get an ImageIcon object for the image of this resource.
148 * @param dim The requested dimensions. Use (-1,-1) for the original size and (width, -1)
149 * to set the width, but otherwise scale the image proportionally.
150 * @param multiResolution If true, return a multi-resolution image
151 * (java.awt.image.MultiResolutionImage in Java 9), otherwise a plain {@link BufferedImage}.
152 * When running Java 8, this flag has no effect and a plain image will be returned in any case.
153 * @param resizeMode how to size/resize the image
154 * @return ImageIcon object for the image of this resource, scaled according to dim
155 * @since 12722
156 */
157 ImageIcon getImageIcon(Dimension dim, boolean multiResolution, ImageResizeMode resizeMode) {
158 return getImageIconAlreadyScaled(GuiSizesHelper.getDimensionDpiAdjusted(dim), multiResolution, false, resizeMode);
159 }
160
161 /**
162 * Get an ImageIcon object for the image of this resource. A potential UI scaling is assumed
163 * to be already taken care of, so dim is already scaled accordingly.
164 * @param dim The requested dimensions. Use (-1,-1) for the original size and (width, -1)
165 * to set the width, but otherwise scale the image proportionally.
166 * @param multiResolution If true, return a multi-resolution image
167 * (java.awt.image.MultiResolutionImage in Java 9), otherwise a plain {@link BufferedImage}.
168 * When running Java 8, this flag has no effect and a plain image will be returned in any case.
169 * @param highResolution whether the high resolution variant should be used for overlays
170 * @param resizeMode how to size/resize the image
171 * @return ImageIcon object for the image of this resource, scaled according to dim
172 */
173 ImageIcon getImageIconAlreadyScaled(Dimension dim, boolean multiResolution, boolean highResolution, ImageResizeMode resizeMode) {
174 CheckParameterUtil.ensureThat((dim.width > 0 || dim.width == -1) && (dim.height > 0 || dim.height == -1),
175 () -> dim + " is invalid");
176
177 if (resizeMode == null && svg != null) {
178 // upscale SVG icons
179 resizeMode = ImageResizeMode.AUTO;
180 } else if (resizeMode == null) {
181 resizeMode = ImageResizeMode.BOUNDED;
182 }
183 final int cacheKey = resizeMode.cacheKey(dim);
184 BufferedImage img = imgCache.get(cacheKey);
185 if (img == null) {
186 if (svg != null) {
187 img = ImageProvider.createImageFromSvg(svg, dim, resizeMode);
188 if (img == null) {
189 return null;
190 }
191 } else {
192 if (baseImage == null) throw new AssertionError();
193 ImageIcon icon = new ImageIcon(baseImage);
194 img = resizeMode.createBufferedImage(dim, new Dimension(icon.getIconWidth(), icon.getIconHeight()),
195 null, icon.getImage());
196 }
197 if (overlayInfo != null) {
198 for (ImageOverlay o : overlayInfo) {
199 o.process(img, highResolution);
200 }
201 }
202 if (isDisabled) {
203 //Use default Swing functionality to make icon look disabled by applying grayscaling filter.
204 Icon disabledIcon = UIManager.getLookAndFeel().getDisabledIcon(null, new ImageIcon(img));
205 if (disabledIcon == null) {
206 return null;
207 }
208
209 //Convert Icon to ImageIcon with BufferedImage inside
210 img = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
211 disabledIcon.paintIcon(new JPanel(), img.getGraphics(), 0, 0);
212 }
213 imgCache.put(cacheKey, img);
214 }
215
216 if (!multiResolution)
217 return new ImageIcon(img);
218 else {
219 try {
220 Image mrImg = HiDPISupport.getMultiResolutionImage(img, this, resizeMode);
221 return new ImageIcon(mrImg);
222 } catch (NoClassDefFoundError e) {
223 Logging.trace(e);
224 return new ImageIcon(img);
225 }
226 }
227 }
228
229 /**
230 * Get image icon with a certain maximum size. The image is scaled down
231 * to fit maximum dimensions. (Keeps aspect ratio)
232 * <p>
233 * Will return a multi-resolution image by default (if possible).
234 *
235 * @param maxSize The maximum size. One of the dimensions (width or height) can be -1,
236 * which means it is not bounded.
237 * @return ImageIcon object for the image of this resource, scaled down if needed, according to maxSize
238 */
239 public ImageIcon getImageIconBounded(Dimension maxSize) {
240 return getImageIcon(maxSize, true, ImageResizeMode.BOUNDED);
241 }
242
243 /**
244 * Returns an {@link ImageIcon} for the given map image, at the specified size.
245 * Uses a cache to improve performance.
246 * @param iconSize size in pixels
247 * @return an {@code ImageIcon} for the given map image, at the specified size
248 */
249 public ImageIcon getPaddedIcon(Dimension iconSize) {
250 return getImageIcon(iconSize, true, ImageResizeMode.PADDED);
251 }
252
253 @Override
254 public String toString() {
255 return "ImageResource ["
256 + (svg != null ? "svg=" + svg : "")
257 + (baseImage != null ? "baseImage=" + baseImage : "") + ']';
258 }
259}
Note: See TracBrowser for help on using the repository browser.