1 /* 2 * Copyright (c) 2011, 2013, 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 26 package sun.awt.image; 27 28 import java.awt.*; 29 import java.lang.ref.*; 30 import java.util.*; 31 import java.util.concurrent.locks.*; 32 33 import sun.awt.AppContext; 34 /** 35 * ImageCache - A fixed pixel count sized cache of Images keyed by arbitrary set of arguments. All images are held with 36 * SoftReferences so they will be dropped by the GC if heap memory gets tight. When our size hits max pixel count least 37 * recently requested images are removed first. 38 */ 39 final public class ImageCache { 40 41 abstract static class RecyclableSingleton<T> { 42 final T get() { 43 final AppContext appContext = AppContext.getAppContext(); 44 SoftReference<T> ref = (SoftReference<T>) appContext.get(this); 45 if (ref != null) { 46 final T object = ref.get(); 47 if (object != null) return object; 48 } 49 final T object = getInstance(); 50 ref = new SoftReference<T>(object); 51 appContext.put(this, ref); 52 return object; 53 } 54 55 void reset() { 56 AppContext.getAppContext().remove(this); 57 } 58 59 abstract T getInstance(); 60 } 61 62 // Ordered Map keyed by args hash, ordered by most recent accessed entry. 63 private final LinkedHashMap<PixelsKey, ImageSoftReference> map = new LinkedHashMap<>(16, 0.75f, true); 64 65 // Maximum number of pixels to cache, this is used if maxCount 66 private final int maxPixelCount; 67 // The current number of pixels stored in the cache 68 private int currentPixelCount = 0; 69 70 // Lock for concurrent access to map 71 private final ReadWriteLock lock = new ReentrantReadWriteLock(); 72 // Reference queue for tracking lost softreferences to images in the cache 73 private final ReferenceQueue<Image> referenceQueue = new ReferenceQueue<>(); 74 75 // Singleton Instance 76 private static final RecyclableSingleton<ImageCache> instance = new RecyclableSingleton<ImageCache>() { 77 @Override 78 protected ImageCache getInstance() { 79 return new ImageCache(); 80 } 81 }; 82 public static ImageCache getInstance() { 83 return instance.get(); 84 } 85 86 ImageCache(final int maxPixelCount) { 87 this.maxPixelCount = maxPixelCount; 88 } 89 90 ImageCache() { 91 this((8 * 1024 * 1024) / 4); // 8Mb of pixels 92 } 93 94 public void flush() { 95 lock.writeLock().lock(); 96 try { 97 map.clear(); 98 } finally { 99 lock.writeLock().unlock(); 100 } 101 } 102 103 public Image getImage(final PixelsKey key){ 104 final ImageSoftReference ref; 105 lock.readLock().lock(); 106 try { 107 ref = map.get(key); 108 } finally { 109 lock.readLock().unlock(); 110 } 111 return ref == null ? null : ref.get(); 112 } 113 114 /** 115 * Sets the cached image for the specified constraints. 116 * 117 * @param key The key with which the specified image is to be associated 118 * @param image The image to store in cache 119 * @return true if the image could be cached, false otherwise. 120 */ 121 public boolean setImage(final PixelsKey key, final Image image) { 122 123 lock.writeLock().lock(); 124 try { 125 ImageSoftReference ref = map.get(key); 126 127 // check if currently in map 128 if (ref != null) { 129 if (ref.get() != null) { 130 return true; 131 } 132 // soft image has been removed 133 currentPixelCount -= key.getPixelCount(); 134 map.remove(key); 135 }; 136 137 138 // add new image to pixel count 139 final int newPixelCount = key.getPixelCount(); 140 currentPixelCount += newPixelCount; 141 // clean out lost references if not enough space 142 if (currentPixelCount > maxPixelCount) { 143 while ((ref = (ImageSoftReference)referenceQueue.poll()) != null) { 144 //reference lost 145 map.remove(ref.key); 146 currentPixelCount -= ref.key.getPixelCount(); 147 } 148 } 149 150 // remove old items till there is enough free space 151 if (currentPixelCount > maxPixelCount) { 152 final Iterator<Map.Entry<PixelsKey, ImageSoftReference>> mapIter = map.entrySet().iterator(); 153 while ((currentPixelCount > maxPixelCount) && mapIter.hasNext()) { 154 final Map.Entry<PixelsKey, ImageSoftReference> entry = mapIter.next(); 155 mapIter.remove(); 156 final Image img = entry.getValue().get(); 157 if (img != null) img.flush(); 158 currentPixelCount -= entry.getValue().key.getPixelCount(); 159 } 160 } 161 162 // finally put new in map 163 map.put(key, new ImageSoftReference(key, image, referenceQueue)); 164 return true; 165 } finally { 166 lock.writeLock().unlock(); 167 } 168 } 169 170 public interface PixelsKey { 171 172 int getPixelCount(); 173 } 174 175 private static class ImageSoftReference extends SoftReference<Image> { 176 177 final PixelsKey key; 178 179 ImageSoftReference(final PixelsKey key, final Image referent, 180 final ReferenceQueue<? super Image> q) { 181 super(referent, q); 182 this.key = key; 183 } 184 } 185 }