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