1 /* 2 * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package sun.swing; 26 27 import sun.awt.image.SurfaceManager; 28 import sun.java2d.SurfaceData; 29 import java.awt.Component; 30 import java.awt.Graphics; 31 import java.awt.Graphics2D; 32 import java.awt.GraphicsConfiguration; 33 import java.awt.Image; 34 import java.awt.geom.AffineTransform; 35 import java.awt.image.AbstractMultiResolutionImage; 36 import java.awt.image.BufferedImage; 37 import java.awt.image.ImageObserver; 38 import java.awt.image.VolatileImage; 39 import java.util.Arrays; 40 import java.util.HashMap; 41 import java.util.Map; 42 43 /** 44 * A base class used for icons or images that are expensive to paint. 45 * A subclass will do the following: 46 * <ol> 47 * <li>Invoke <code>paint</code> when you want to paint the image, 48 * if you are implementing <code>Icon</code> you'll invoke this from 49 * <code>paintIcon</code>. 50 * The args argument is useful when additional state is needed. 51 * <li>Override <code>paintToImage</code> to render the image. The code that 52 * lives here is equivalent to what previously would go in 53 * <code>paintIcon</code>, for an <code>Icon</code>. 54 * </ol> 55 * The two ways to use this class are: 56 * <ol> 57 * <li>Invoke <code>paint</code> to draw the cached reprensentation at 58 * the specified location. 59 * <li>Invoke <code>getImage</code> to get the cached reprensentation and 60 * draw the image yourself. This is primarly useful when you are not 61 * using <code>VolatileImage</code>. 62 * </ol> 63 * 64 * 65 */ 66 public abstract class CachedPainter { 67 // CacheMap maps from class to ImageCache. 68 private static final Map<Object,ImageCache> cacheMap = new HashMap<>(); 69 70 private static ImageCache getCache(Object key) { 71 synchronized(CachedPainter.class) { 72 ImageCache cache = cacheMap.get(key); 73 if (cache == null) { 74 if (key == PainterMultiResolutionCachedImage.class) { 75 cache = new ImageCache(32); 76 } else { 77 cache = new ImageCache(1); 78 } 79 cacheMap.put(key, cache); 80 } 81 return cache; 82 } 83 } 84 85 /** 86 * Creates an instance of <code>CachedPainter</code> that will cache up 87 * to <code>cacheCount</code> images of this class. 88 * 89 * @param cacheCount Max number of images to cache 90 */ 91 public CachedPainter(int cacheCount) { 92 getCache(getClass()).setMaxCount(cacheCount); 93 } 94 95 /** 96 * Renders the cached image to the passed in <code>Graphic</code>. 97 * If there is no cached image <code>paintToImage</code> will be invoked. 98 * <code>paintImage</code> is invoked to paint the cached image. 99 * 100 * @param c Component rendering to, this may be null. 101 * @param g Graphics to paint to 102 * @param x X-coordinate to render to 103 * @param y Y-coordinate to render to 104 * @param w Width to render in 105 * @param h Height to render in 106 * @param args Variable arguments that will be passed to paintToImage 107 */ 108 public void paint(Component c, Graphics g, int x, 109 int y, int w, int h, Object... args) { 110 if (w <= 0 || h <= 0) { 111 return; 112 } 113 synchronized (CachedPainter.class) { 114 paint0(c, g, x, y, w, h, args); 115 } 116 } 117 118 private Image getImage(Object key, Component c, 119 int baseWidth, int baseHeight, 120 int w, int h, Object... args) { 121 GraphicsConfiguration config = getGraphicsConfiguration(c); 122 ImageCache cache = getCache(key); 123 Image image = cache.getImage(key, config, w, h, args); 124 int attempts = 0; 125 VolatileImage volatileImage = (image instanceof VolatileImage) 126 ? (VolatileImage) image 127 : null; 128 do { 129 boolean draw = false; 130 if (volatileImage != null) { 131 // See if we need to recreate the image 132 switch (volatileImage.validate(config)) { 133 case VolatileImage.IMAGE_INCOMPATIBLE: 134 volatileImage.flush(); 135 image = null; 136 break; 137 case VolatileImage.IMAGE_RESTORED: 138 draw = true; 139 break; 140 } 141 } 142 if (image == null) { 143 // Recreate the image 144 if( config != null && (w != baseHeight || h != baseWidth)) { 145 AffineTransform tx = config.getDefaultTransform(); 146 double sx = tx.getScaleX(); 147 double sy = tx.getScaleY(); 148 if ( Double.compare(sx, 1) != 0 || 149 Double.compare(sy, 1) != 0) { 150 if (Math.abs(sx * baseWidth - w) < 1 && 151 Math.abs(sy * baseHeight - h) < 1) { 152 w = baseWidth; 153 h = baseHeight; 154 } else { 155 w = (int)Math.ceil(w / sx); 156 h = (int)Math.ceil(w / sy); 157 } 158 } 159 } 160 image = createImage(c, w, h, config, args); 161 cache.setImage(key, config, w, h, args, image); 162 draw = true; 163 volatileImage = (image instanceof VolatileImage) 164 ? (VolatileImage) image 165 : null; 166 } 167 if (draw) { 168 // Render to the Image 169 Graphics2D g2 = (Graphics2D) image.getGraphics(); 170 if (volatileImage == null) { 171 if ((w != baseWidth || h != baseHeight)) { 172 g2.scale((double) w / baseWidth, 173 (double) h / baseHeight); 174 } 175 paintToImage(c, image, g2, baseWidth, baseHeight, args); 176 } else { 177 SurfaceData sd = SurfaceManager.getManager(volatileImage) 178 .getPrimarySurfaceData(); 179 double sx = sd.getDefaultScaleX(); 180 double sy = sd.getDefaultScaleY(); 181 if ( Double.compare(sx, 1) != 0 || 182 Double.compare(sy, 1) != 0) { 183 g2.scale(1 / sx, 1 / sy); 184 } 185 paintToImage(c, image, g2, (int)Math.ceil(w * sx), 186 (int)Math.ceil(h * sy), args); 187 } 188 g2.dispose(); 189 } 190 191 // If we did this 3 times and the contents are still lost 192 // assume we're painting to a VolatileImage that is bogus and 193 // give up. Presumably we'll be called again to paint. 194 } while ((volatileImage != null) && 195 volatileImage.contentsLost() && ++attempts < 3); 196 197 return image; 198 } 199 200 private void paint0(Component c, Graphics g, int x, 201 int y, int w, int h, Object... args) { 202 Object key = getClass(); 203 GraphicsConfiguration config = getGraphicsConfiguration(c); 204 ImageCache cache = getCache(key); 205 Image image = cache.getImage(key, config, w, h, args); 206 207 if (image == null) { 208 image = new PainterMultiResolutionCachedImage(w, h); 209 cache.setImage(key, config, w, h, args, image); 210 } 211 212 if (image instanceof PainterMultiResolutionCachedImage) { 213 ((PainterMultiResolutionCachedImage) image).setParams(c, args); 214 } 215 216 // Render to the passed in Graphics 217 paintImage(c, g, x, y, w, h, image, args); 218 } 219 220 /** 221 * Paints the representation to cache to the supplied Graphics. 222 * 223 * @param c Component painting to, may be null. 224 * @param image Image to paint to 225 * @param g Graphics to paint to, obtained from the passed in Image. 226 * @param w Width to paint to 227 * @param h Height to paint to 228 * @param args Arguments supplied to <code>paint</code> 229 */ 230 protected abstract void paintToImage(Component c, Image image, Graphics g, 231 int w, int h, Object[] args); 232 233 234 /** 235 * Paints the image to the specified location. 236 * 237 * @param c Component painting to 238 * @param g Graphics to paint to 239 * @param x X coordinate to paint to 240 * @param y Y coordinate to paint to 241 * @param w Width to paint to 242 * @param h Height to paint to 243 * @param image Image to paint 244 * @param args Arguments supplied to <code>paint</code> 245 */ 246 protected void paintImage(Component c, Graphics g, 247 int x, int y, int w, int h, Image image, 248 Object[] args) { 249 g.drawImage(image, x, y, null); 250 } 251 252 /** 253 * Creates the image to cache. This returns an opaque image, subclasses 254 * that require translucency or transparency will need to override this 255 * method. 256 * 257 * @param c Component painting to 258 * @param w Width of image to create 259 * @param h Height to image to create 260 * @param config GraphicsConfiguration that will be 261 * rendered to, this may be null. 262 * @param args Arguments passed to paint 263 */ 264 protected Image createImage(Component c, int w, int h, 265 GraphicsConfiguration config, Object[] args) { 266 if (config == null) { 267 return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 268 } 269 return config.createCompatibleVolatileImage(w, h); 270 } 271 272 /** 273 * Clear the image cache 274 */ 275 protected void flush() { 276 synchronized(CachedPainter.class) { 277 getCache(getClass()).flush(); 278 } 279 } 280 281 private GraphicsConfiguration getGraphicsConfiguration(Component c) { 282 if (c == null) { 283 return null; 284 } 285 return c.getGraphicsConfiguration(); 286 } 287 288 class PainterMultiResolutionCachedImage extends AbstractMultiResolutionImage { 289 290 private final int baseWidth; 291 private final int baseHeight; 292 private Component c; 293 private Object[] args; 294 295 public PainterMultiResolutionCachedImage(int baseWidth, int baseHeight) { 296 this.baseWidth = baseWidth; 297 this.baseHeight = baseHeight; 298 } 299 300 public void setParams(Component c, Object[] args) { 301 this.c = c; 302 this.args = args; 303 } 304 305 @Override 306 public int getWidth(ImageObserver observer) { 307 return baseWidth; 308 } 309 310 @Override 311 public int getHeight(ImageObserver observer) { 312 return baseHeight; 313 } 314 315 @Override 316 public Image getResolutionVariant(double destWidth, double destHeight) { 317 int w = (int) Math.ceil(destWidth); 318 int h = (int) Math.ceil(destHeight); 319 return getImage(PainterMultiResolutionCachedImage.class, 320 c, baseWidth, baseHeight, w, h, args); 321 } 322 323 @Override 324 protected Image getBaseImage() { 325 return getResolutionVariant(baseWidth, baseHeight); 326 } 327 328 @Override 329 public java.util.List<Image> getResolutionVariants() { 330 return Arrays.asList(getResolutionVariant(baseWidth, baseHeight)); 331 } 332 } 333 }