1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.scenegraph.utils;
14 import java.awt.GraphicsConfiguration;
15 import java.awt.Image;
16 import java.awt.Transparency;
17 import java.awt.image.VolatileImage;
18 import java.lang.ref.Reference;
19 import java.lang.ref.SoftReference;
20 import java.util.LinkedHashMap;
22 import java.util.concurrent.atomic.AtomicInteger;
25 * @author Antti Villberg
27 public class VolatileImageCache {
29 private static final boolean DEBUG = false;
31 private static VolatileImageCache instance = null;
33 private VolatileImageCache() {
36 public static VolatileImageCache getInstance() {
37 if (instance == null) {
38 instance = new VolatileImageCache();
44 * The max allowed size of the cache in megapixels. The amount of actual
45 * consumed (video) memory is approximately 4 times this.
47 private static final int MAX_SIZE = 8 * 1024 * 1024;
51 * Current size of the cache in megapixels.
54 private int counter = 0;
56 class VolatileImageProviderImpl implements VolatileImageProvider {
61 private Reference<VolatileImage> imageRef = null;
63 public VolatileImageProviderImpl(int w, int h) {
69 private VolatileImage dereferenceImage() {
70 return imageRef != null ? imageRef.get() : null;
73 public VolatileImage get(GraphicsConfiguration gc, AtomicInteger _validateResult) {
74 int validateResult = VolatileImage.IMAGE_INCOMPATIBLE;
75 VolatileImage vimg = dereferenceImage();
76 //System.out.println("GC: " + gc);
78 validateResult = vimg.validate(gc);
80 if (validateResult == VolatileImage.IMAGE_RESTORED) {
82 System.out.println("VOLATILE IMAGE RESTORED for PROVIDER " + this);
84 if (validateResult == VolatileImage.IMAGE_INCOMPATIBLE) {
86 System.out.println("(RE)CREATING VOLATILE IMAGE FOR PROVIDER " + this);
87 vimg = gc.createCompatibleVolatileImage(w, h, Transparency.TRANSLUCENT);
90 throw new IllegalStateException(this + ": got null VolatileImage from GraphicsConfiguration " + gc);
92 this.imageRef = new SoftReference<VolatileImage>(vimg);
94 // Implement move to front required for LRU
95 synchronized (cache) {
96 Object oldObject = cache.remove(this);
97 boolean wasCached = oldObject == this;
98 cache.put(this, this);
103 debug(this, cache.size(), size - w*h, size, "created new image");
106 // debug(this, cache.size(), size, size, "LRU move-to-front");
112 if (_validateResult != null)
113 _validateResult.set(validateResult);
118 public void dispose() {
120 synchronized (cache) {
122 Object removed = cache.remove(this);
123 if (removed == this) {
128 debug(this, cache.size(), newSize + w*h, newSize, "explicitly disposed");
130 if (imageRef != null) {
131 flushImageRef(imageRef);
138 private static final String MSG = "[provider #%-10d @%-8x][image (%-3d,%-3d)][cache entries=%-4d, old size=%-10d, new size=%-10d, size change=%-+6d] %s";
140 private static void debug(VolatileImageProviderImpl i, int entries, int oldSize, int newSize, String msg) {
142 int sizeChange = newSize - oldSize;
143 String s = String.format(MSG, i.id, i.hashCode(), i.w, i.h, entries, oldSize, newSize, sizeChange, msg);
144 System.out.println(s);
148 private final Map<VolatileImageProviderImpl,VolatileImageProviderImpl> cache = new LinkedHashMap<VolatileImageProviderImpl, VolatileImageProviderImpl>(50, .75F, true) {
149 private static final long serialVersionUID = 5946026822169837291L;
151 protected boolean removeEldestEntry(Map.Entry<VolatileImageProviderImpl,VolatileImageProviderImpl> eldest) {
152 boolean result = size > MAX_SIZE;
154 // new Exception().printStackTrace();
155 VolatileImageProviderImpl impl = eldest.getKey();
156 size -= impl.w*impl.h;
158 //debug(impl, impl.imageId, cache.size(), oldSize, size, "cache full, dumping oldest");
160 flushImageRef(impl.imageRef);
161 impl.imageRef = null;
167 private static final void flushImageRef(Reference<? extends Image> imageRef) {
168 if (imageRef != null) {
169 Image img = imageRef.get();
176 VolatileImageProvider create(int w, int h) {
177 // System.out.println("VolatileImageProvider.create(" + w + ", " + h + ")");
178 VolatileImageProviderImpl result = new VolatileImageProviderImpl(w, h);