package org.openflexo.fge.view; //http://www.javaresearch.org/source/jdk150/javax/swing/plaf/metal/CachedPainter.java.html /* * @(#)CachedPainter.java 1.2 04/02/15 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ import java.awt.Component; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * A base class used for icons or images that are expensive to paint. A subclass will do the following: * <ol> * <li>Invoke <code>paint</code> when you want to paint the image, if you are implementing <code>Icon</code> you'll invoke this from * <code>paintIcon</code>. The args argument is useful when additional state is needed. * <li>Override <code>paintToImage</code> to render the image. The code that lives here is equivalent to what previously would go in * <code>paintIcon</code>, for an <code>Icon</code>. * </ol> * * @version @(#)CachedPainter.java 1.2 04/02/15 */ abstract class CachedPainter { // CacheMap maps from class to Cache. private static final Map<Object, Cache> cacheMap = new HashMap<Object, Cache>(); private static Cache getCache(Object key) { synchronized (cacheMap) { Cache cache = cacheMap.get(key); if (cache == null) { cache = new Cache(1); cacheMap.put(key, cache); } return cache; } } /** * Creates an instance of <code>CachedPainter</code> that will cache up to <code>cacheCount</code> images of this class. * * @param cacheCount * Max number of images to cache */ public CachedPainter(int cacheCount) { getCache(getClass()).setMaxCount(cacheCount); } /** * Renders the cached image to the the passed in <code>Graphic</code>. If there is no cached image <code>paintToImage</code> will be * invoked. <code>paintImage</code> is invoked to paint the cached image. */ protected void paint(Component c, Graphics g, int x, int y, int w, int h, Object... args) { if (w <= 0 || h <= 0) { return; } Object key = getClass(); GraphicsConfiguration config = c.getGraphicsConfiguration(); Cache cache = getCache(key); Image image = cache.getImage(key, config, w, h, args); int attempts = 0; do { boolean draw = false; if (image instanceof VolatileImage) { // See if we need to recreate the image switch (((VolatileImage) image).validate(config)) { case VolatileImage.IMAGE_INCOMPATIBLE: ((VolatileImage) image).flush(); image = null; break; case VolatileImage.IMAGE_RESTORED: draw = true; break; } } if (image == null) { // Recreate the image image = createImage(c, w, h, config); cache.setImage(key, config, w, h, args, image); draw = true; } if (draw) { // Render to the Image Graphics g2 = image.getGraphics(); paintToImage(c, g2, w, h, args); g2.dispose(); } // Render to the passed in Graphics paintImage(c, g, x, y, w, h, image, args); // If we did this 3 times and the contents are still lost // assume we're painting to a VolatileImage that is bogus and // give up. Presumably we'll be called again to paint. } while (image instanceof VolatileImage && ((VolatileImage) image).contentsLost() && ++attempts < 3); } /** * Paints the representation to cache to the supplied Graphics. * * @param c * Component painting to * @param g * Graphics to paint to * @param w * Width to paint to * @param h * Height to paint to * @param args * Arguments supplied to <code>paint</code> */ protected abstract void paintToImage(Component c, Graphics g, int w, int h, Object[] args); /** * Paints the image to the specified location. * * @param c * Component painting to * @param g * Graphics to paint to * @param x * X coordinate to paint to * @param y * Y coordinate to paint to * @param w * Width to paint to * @param h * Height to paint to * @param image * Image to paint * @param args * Arguments supplied to <code>paint</code> */ protected void paintImage(Component c, Graphics g, int x, int y, int w, int h, Image image, Object[] args) { g.drawImage(image, x, y, null); } /** * Creates the image to cache. This returns an opaque image, subclasses that require translucency or transparency will need to override * this method. * * @param c * Component painting to * @param w * Width of image to create * @param h * Height to image to create * @param config * GraphicsConfiguration that will be rendered to, this may be null. */ protected Image createImage(Component c, int w, int h, GraphicsConfiguration config) { if (config == null) { return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); } return config.createCompatibleVolatileImage(w, h); } /** * Cache is used to cache an image based on a set of arguments. */ private static class Cache { // Maximum number of entries to cache private int maxCount; // The entries. private java.util.List<SoftReference<Entry>> entries; Cache(int maxCount) { this.maxCount = maxCount; entries = new ArrayList<SoftReference<Entry>>(maxCount); } void setMaxCount(int maxCount) { this.maxCount = maxCount; } private Entry getEntry(Object key, GraphicsConfiguration config, int w, int h, Object[] args) { synchronized (this) { Entry entry; for (int counter = entries.size() - 1; counter >= 0; counter--) { entry = entries.get(counter).get(); if (entry == null) { // SoftReference was invalidated, remove the entry entries.remove(counter); } else if (entry.equals(config, w, h, args)) { // Found the entry, return it. return entry; } } // Entry doesn't exist entry = new Entry(config, w, h, args); if (entries.size() == maxCount) { entries.remove(0); } entries.add(new SoftReference<Entry>(entry)); return entry; } } /** * Returns the cached Image, or null, for the specified arguments. */ public Image getImage(Object key, GraphicsConfiguration config, int w, int h, Object[] args) { Entry entry = getEntry(key, config, w, h, args); return entry.getImage(); } /** * Sets the cached image for the specified constraints. */ public void setImage(Object key, GraphicsConfiguration config, int w, int h, Object[] args, Image image) { Entry entry = getEntry(key, config, w, h, args); entry.setImage(image); } /** * Caches set of arguments and Image. */ private static class Entry { private GraphicsConfiguration config; private Object[] args; private Image image; private int w; private int h; Entry(GraphicsConfiguration config, int w, int h, Object[] args) { this.config = config; this.args = args; this.w = w; this.h = h; } public void setImage(Image image) { this.image = image; } public Image getImage() { return image; } @Override public String toString() { String value = super.toString() + "[ graphicsConfig=" + config + ", image=" + image + ", w=" + w + ", h=" + h; if (args != null) { for (int counter = 0; counter < args.length; counter++) { value += ", " + args[counter]; } } value += "]"; return value; } public boolean equals(GraphicsConfiguration config, int w, int h, Object[] args) { if (this.w == w && this.h == h && (this.config != null && this.config.equals(config) || this.config == null && config == null)) { if (this.args == null && args == null) { return true; } if (this.args != null && args != null && this.args.length == args.length) { for (int counter = args.length - 1; counter >= 0; counter--) { if (!this.args[counter].equals(args[counter])) { return false; } } return true; } } return false; } } } }