1. /*
  2. * @(#)CachedPainter.java 1.2 04/02/15
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.metal;
  8. import java.awt.*;
  9. import java.awt.image.*;
  10. import javax.swing.Icon;
  11. import java.lang.ref.SoftReference;
  12. import java.util.*;
  13. /**
  14. * A base class used for icons or images that are expensive to paint.
  15. * A subclass will do the following:
  16. * <ol>
  17. * <li>Invoke <code>paint</code> when you want to paint the image,
  18. * if you are implementing <code>Icon</code> you'll invoke this from
  19. * <code>paintIcon</code>.
  20. * The args argument is useful when additional state is needed.
  21. * <li>Override <code>paintToImage</code> to render the image. The code that
  22. * lives here is equivalent to what previously would go in
  23. * <code>paintIcon</code>, for an <code>Icon</code>.
  24. * </ol>
  25. *
  26. * @version @(#)CachedPainter.java 1.2 04/02/15
  27. */
  28. abstract class CachedPainter {
  29. // CacheMap maps from class to Cache.
  30. private static final Map<Object,Cache> cacheMap =
  31. new HashMap<Object,Cache>();
  32. private static Cache getCache(Object key) {
  33. synchronized(cacheMap) {
  34. Cache cache = cacheMap.get(key);
  35. if (cache == null) {
  36. cache = new Cache(1);
  37. cacheMap.put(key, cache);
  38. }
  39. return cache;
  40. }
  41. }
  42. /**
  43. * Creates an instance of <code>CachedPainter</code> that will cache up
  44. * to <code>cacheCount</code> images of this class.
  45. *
  46. * @param cacheCount Max number of images to cache
  47. */
  48. public CachedPainter(int cacheCount) {
  49. getCache(getClass()).setMaxCount(cacheCount);
  50. }
  51. /**
  52. * Renders the cached image to the the passed in <code>Graphic</code>.
  53. * If there is no cached image <code>paintToImage</code> will be invoked.
  54. * <code>paintImage</code> is invoked to paint the cached image.
  55. */
  56. protected void paint(Component c, Graphics g, int x,
  57. int y, int w, int h, Object... args) {
  58. if (w <= 0 || h <= 0) {
  59. return;
  60. }
  61. Object key = getClass();
  62. GraphicsConfiguration config = c.getGraphicsConfiguration();
  63. Cache cache = getCache(key);
  64. Image image = cache.getImage(key, config, w, h, args);
  65. int attempts = 0;
  66. do {
  67. boolean draw = false;
  68. if (image instanceof VolatileImage) {
  69. // See if we need to recreate the image
  70. switch (((VolatileImage)image).validate(config)) {
  71. case VolatileImage.IMAGE_INCOMPATIBLE:
  72. ((VolatileImage)image).flush();
  73. image = null;
  74. break;
  75. case VolatileImage.IMAGE_RESTORED:
  76. draw = true;
  77. break;
  78. }
  79. }
  80. if (image == null) {
  81. // Recreate the image
  82. image = createImage(c, w, h, config);
  83. cache.setImage(key, config, w, h, args, image);
  84. draw = true;
  85. }
  86. if (draw) {
  87. // Render to the Image
  88. Graphics g2 = image.getGraphics();
  89. paintToImage(c, g2, w, h, args);
  90. g2.dispose();
  91. }
  92. // Render to the passed in Graphics
  93. paintImage(c, g, x, y, w, h, image, args);
  94. // If we did this 3 times and the contents are still lost
  95. // assume we're painting to a VolatileImage that is bogus and
  96. // give up. Presumably we'll be called again to paint.
  97. } while ((image instanceof VolatileImage) &&
  98. ((VolatileImage)image).contentsLost() && ++attempts < 3);
  99. }
  100. /**
  101. * Paints the representation to cache to the supplied Graphics.
  102. *
  103. * @param c Component painting to
  104. * @param g Graphics to paint to
  105. * @param w Width to paint to
  106. * @param h Height to paint to
  107. * @param args Arguments supplied to <code>paint</code>
  108. */
  109. protected abstract void paintToImage(Component c, Graphics g,
  110. int w, int h, Object[] args);
  111. /**
  112. * Paints the image to the specified location.
  113. *
  114. * @param c Component painting to
  115. * @param g Graphics to paint to
  116. * @param x X coordinate to paint to
  117. * @param y Y coordinate to paint to
  118. * @param w Width to paint to
  119. * @param h Height to paint to
  120. * @param image Image to paint
  121. * @param args Arguments supplied to <code>paint</code>
  122. */
  123. protected void paintImage(Component c, Graphics g,
  124. int x, int y, int w, int h, Image image,
  125. Object[] args) {
  126. g.drawImage(image, x, y, null);
  127. }
  128. /**
  129. * Creates the image to cache. This returns an opaque image, subclasses
  130. * that require translucency or transparency will need to override this
  131. * method.
  132. *
  133. * @param c Component painting to
  134. * @param w Width of image to create
  135. * @param h Height to image to create
  136. * @param config GraphicsConfiguration that will be
  137. * rendered to, this may be null.
  138. */
  139. protected Image createImage(Component c, int w, int h,
  140. GraphicsConfiguration config) {
  141. if (config == null) {
  142. return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
  143. }
  144. return config.createCompatibleVolatileImage(w, h);
  145. }
  146. /**
  147. * Cache is used to cache an image based on a set of arguments.
  148. */
  149. private static class Cache {
  150. // Maximum number of entries to cache
  151. private int maxCount;
  152. // The entries.
  153. private java.util.List<SoftReference<Entry>> entries;
  154. Cache(int maxCount) {
  155. this.maxCount = maxCount;
  156. entries = new ArrayList<SoftReference<Entry>>(maxCount);
  157. }
  158. void setMaxCount(int maxCount) {
  159. this.maxCount = maxCount;
  160. }
  161. private Entry getEntry(Object key, GraphicsConfiguration config,
  162. int w, int h, Object[] args) {
  163. synchronized(this) {
  164. Entry entry;
  165. for (int counter = entries.size() - 1; counter >= 0;counter--){
  166. entry = entries.get(counter).get();
  167. if (entry == null) {
  168. // SoftReference was invalidated, remove the entry
  169. entries.remove(counter);
  170. }
  171. else if (entry.equals(config, w, h, args)) {
  172. // Found the entry, return it.
  173. return entry;
  174. }
  175. }
  176. // Entry doesn't exist
  177. entry = new Entry(config, w, h, args);
  178. if (entries.size() == maxCount) {
  179. entries.remove(0);
  180. }
  181. entries.add(new SoftReference<Entry>(entry));
  182. return entry;
  183. }
  184. }
  185. /**
  186. * Returns the cached Image, or null, for the specified arguments.
  187. */
  188. public Image getImage(Object key, GraphicsConfiguration config,
  189. int w, int h, Object[] args) {
  190. Entry entry = getEntry(key, config, w, h, args);
  191. return entry.getImage();
  192. }
  193. /**
  194. * Sets the cached image for the specified constraints.
  195. */
  196. public void setImage(Object key, GraphicsConfiguration config,
  197. int w, int h, Object[] args, Image image) {
  198. Entry entry = getEntry(key, config, w, h, args);
  199. entry.setImage(image);
  200. }
  201. /**
  202. * Caches set of arguments and Image.
  203. */
  204. private static class Entry {
  205. private GraphicsConfiguration config;
  206. private Object[] args;
  207. private Image image;
  208. private int w;
  209. private int h;
  210. Entry(GraphicsConfiguration config, int w, int h, Object[] args) {
  211. this.config = config;
  212. this.args = args;
  213. this.w = w;
  214. this.h = h;
  215. }
  216. public void setImage(Image image) {
  217. this.image = image;
  218. }
  219. public Image getImage() {
  220. return image;
  221. }
  222. public String toString() {
  223. String value = super.toString() +
  224. "[ graphicsConfig=" + config +
  225. ", image=" + image +
  226. ", w=" + w + ", h=" + h;
  227. if (args != null) {
  228. for (int counter = 0; counter < args.length; counter++) {
  229. value += ", " + args[counter];
  230. }
  231. }
  232. value += "]";
  233. return value;
  234. }
  235. public boolean equals(GraphicsConfiguration config, int w, int h,
  236. Object[] args){
  237. if (this.w == w && this.h == h &&
  238. ((this.config != null && this.config.equals(config)) ||
  239. (this.config == null && config == null))) {
  240. if (this.args == null && args == null) {
  241. return true;
  242. }
  243. if (this.args != null && args != null &&
  244. this.args.length == args.length) {
  245. for (int counter = args.length - 1; counter >= 0;
  246. counter--) {
  247. if (!this.args[counter].equals(args[counter])) {
  248. return false;
  249. }
  250. }
  251. return true;
  252. }
  253. }
  254. return false;
  255. }
  256. }
  257. }
  258. }