KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > java > swing > plaf > nimbus > ImageCache


1 /*
2  * @(#)ImageCache.java 1.3 08/02/04
3  *
4  * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package com.sun.java.swing.plaf.nimbus;
8
9 import java.awt.GraphicsConfiguration JavaDoc;
10 import java.awt.Image JavaDoc;
11 import java.lang.ref.ReferenceQueue JavaDoc;
12 import java.lang.ref.SoftReference JavaDoc;
13 import java.util.Arrays JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.LinkedHashMap JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.concurrent.locks.ReadWriteLock JavaDoc;
18 import java.util.concurrent.locks.ReentrantReadWriteLock JavaDoc;
19
20 /**
21  * ImageCache - A fixed pixel count sized cache of Images keyed by arbitrary set of arguments. All images are held with
22  * SoftReferences so they will be dropped by the GC if heap memory gets tight. When our size hits max pixel count least
23  * recently requested images are removed first.
24  *
25  * @author Created by Jasper Potts (Aug 7, 2007)
26  * @version 1.0
27  */

28 class ImageCache {
29     // Ordered Map keyed by args hash, ordered by most recent accessed entry.
30
private final LinkedHashMap JavaDoc<Integer JavaDoc, PixelCountSoftReference> map =
31             new LinkedHashMap JavaDoc<Integer JavaDoc, PixelCountSoftReference>(16, 0.75f, true);
32     // Maximum number of pixels to cache, this is used if maxCount
33
private final int maxPixelCount;
34     // Maximum cached image size in pxiels
35
private final int maxSingleImagePixelSize;
36     // The current number of pixels stored in the cache
37
private int currentPixelCount = 0;
38     // Lock for concurrent access to map
39
private ReadWriteLock JavaDoc lock = new ReentrantReadWriteLock JavaDoc();
40     // Reference queue for tracking lost softreferences to images in the cache
41
private ReferenceQueue JavaDoc<Image JavaDoc> referenceQueue = new ReferenceQueue JavaDoc<Image JavaDoc>();
42     // Singleton Instance
43
private static final ImageCache instance = new ImageCache();
44
45
46     /** Get static singleton instance */
47     static ImageCache getInstance() {
48         return instance;
49     }
50
51     public ImageCache() {
52         this.maxPixelCount = (8 * 1024 * 1024) / 4; // 8Mb of pixels
53
this.maxSingleImagePixelSize = 300 * 300;
54     }
55
56     public ImageCache(int maxPixelCount, int maxSingleImagePixelSize) {
57         this.maxPixelCount = maxPixelCount;
58         this.maxSingleImagePixelSize = maxSingleImagePixelSize;
59     }
60
61     /** Clear the cache */
62     public void flush() {
63         lock.readLock().lock();
64         try {
65             map.clear();
66         } finally {
67             lock.readLock().unlock();
68         }
69     }
70
71     /**
72      * Check if the image size is to big to be stored in the cache
73      *
74      * @param w The image width
75      * @param h The image height
76      * @return True if the image size is less than max
77      */

78     public boolean isImageCachable(int w, int h) {
79         return (w * h) < maxSingleImagePixelSize;
80     }
81
82     /**
83      * Get the cached image for given keys
84      *
85      * @param config The graphics configuration, needed if cached image is a Volatile Image. Used as part of cache key
86      * @param w The image width, used as part of cache key
87      * @param h The image height, used as part of cache key
88      * @param args Other arguments to use as part of the cache key
89      * @return Returns the cached Image, or null there is no cached image for key
90      */

91     public Image JavaDoc getImage(GraphicsConfiguration JavaDoc config, int w, int h, Object JavaDoc... args) {
92         lock.readLock().lock();
93         try {
94             PixelCountSoftReference ref = map.get(hash(config, w, h, args));
95             // check reference has not been lost and the key truly matches, in case of false positive hash match
96
if (ref != null && ref.equals(config,w, h, args)) {
97                 return ref.get();
98             } else {
99                 return null;
100             }
101         } finally {
102             lock.readLock().unlock();
103         }
104     }
105
106     /**
107      * Sets the cached image for the specified constraints.
108      *
109      * @param image The image to store in cache
110      * @param config The graphics configuration, needed if cached image is a Volatile Image. Used as part of cache key
111      * @param w The image width, used as part of cache key
112      * @param h The image height, used as part of cache key
113      * @param args Other arguments to use as part of the cache key
114      * @return true if the image could be cached or false if the image is too big
115      */

116     public boolean setImage(Image JavaDoc image, GraphicsConfiguration JavaDoc config, int w, int h, Object JavaDoc... args) {
117         if (!isImageCachable(w, h)) return false;
118         int hash = hash(config, w, h, args);
119         lock.writeLock().lock();
120         try {
121             PixelCountSoftReference ref = map.get(hash);
122             // check if currently in map
123
if (ref != null && ref.get() == image) {
124                 return true;
125             }
126             // clear out old
127
if (ref != null) {
128                 currentPixelCount -= ref.pixelCount;
129                 map.remove(hash);
130             }
131             // add new image to pixel count
132
int newPixelCount = image.getWidth(null) * image.getHeight(null);
133             currentPixelCount += newPixelCount;
134             // clean out lost references if not enough space
135
if (currentPixelCount > maxPixelCount) {
136                 while ((ref = (PixelCountSoftReference)referenceQueue.poll()) != null){
137                     //reference lost
138
map.remove(ref.hash);
139                     currentPixelCount -= ref.pixelCount;
140                 }
141             }
142             // remove old items till there is enough free space
143
if (currentPixelCount > maxPixelCount) {
144                 Iterator JavaDoc<Map.Entry JavaDoc<Integer JavaDoc, PixelCountSoftReference>> mapIter = map.entrySet().iterator();
145                 while ((currentPixelCount > maxPixelCount) && mapIter.hasNext()) {
146                     Map.Entry JavaDoc<Integer JavaDoc, PixelCountSoftReference> entry = mapIter.next();
147                     mapIter.remove();
148                     Image JavaDoc img = entry.getValue().get();
149                     if (img != null) img.flush();
150                     currentPixelCount -= entry.getValue().pixelCount;
151                 }
152             }
153             // finaly put new in map
154
map.put(hash, new PixelCountSoftReference(image, referenceQueue, newPixelCount,hash, config, w, h, args));
155             return true;
156         } finally {
157             lock.writeLock().unlock();
158         }
159     }
160
161     /** Create a unique hash from all the input */
162     private int hash(GraphicsConfiguration JavaDoc config, int w, int h, Object JavaDoc ... args) {
163         int hash;
164         hash = (config != null ? config.hashCode() : 0);
165         hash = 31 * hash + w;
166         hash = 31 * hash + h;
167         hash = 31 * hash + Arrays.deepHashCode(args);
168         return hash;
169     }
170
171
172     /** Extended SoftReference that stores the pixel count even after the image is lost */
173     private static class PixelCountSoftReference extends SoftReference JavaDoc<Image JavaDoc> {
174         private final int pixelCount;
175         private final int hash;
176         // key parts
177
private final GraphicsConfiguration JavaDoc config;
178         private final int w;
179         private final int h;
180         private final Object JavaDoc[] args;
181
182         public PixelCountSoftReference(Image JavaDoc referent, ReferenceQueue JavaDoc<? super Image JavaDoc> q, int pixelCount, int hash,
183                                        GraphicsConfiguration JavaDoc config, int w, int h, Object JavaDoc[] args) {
184             super(referent, q);
185             this.pixelCount = pixelCount;
186             this.hash = hash;
187             this.config = config;
188             this.w = w;
189             this.h = h;
190             this.args = args;
191         }
192
193         public boolean equals (GraphicsConfiguration JavaDoc config, int w, int h, Object JavaDoc[] args){
194             return config == this.config &&
195                             w == this.w &&
196                             h == this.h &&
197                             Arrays.equals(args, this.args);
198         }
199     }
200 }
201
Popular Tags