]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/VolatileImageCache.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / utils / VolatileImageCache.java
diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/VolatileImageCache.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/utils/VolatileImageCache.java
new file mode 100644 (file)
index 0000000..f7a297c
--- /dev/null
@@ -0,0 +1,182 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.scenegraph.utils;\r
+\r
+import java.awt.GraphicsConfiguration;\r
+import java.awt.Image;\r
+import java.awt.Transparency;\r
+import java.awt.image.VolatileImage;\r
+import java.lang.ref.Reference;\r
+import java.lang.ref.SoftReference;\r
+import java.util.LinkedHashMap;\r
+import java.util.Map;\r
+import java.util.concurrent.atomic.AtomicInteger;\r
+\r
+/**\r
+ * @author Antti Villberg\r
+ */\r
+public class VolatileImageCache {\r
+\r
+    private static final boolean DEBUG = false;\r
+\r
+    private static VolatileImageCache instance = null;\r
+\r
+    private VolatileImageCache() {\r
+    }\r
+\r
+    public static VolatileImageCache getInstance() {\r
+        if (instance == null) {\r
+            instance = new VolatileImageCache();\r
+        }\r
+        return instance;\r
+    }\r
+\r
+    /**\r
+     * The max allowed size of the cache in megapixels. The amount of actual\r
+     * consumed (video) memory is approximately 4 times this.\r
+     */\r
+    private static final int MAX_SIZE = 8 * 1024 * 1024;\r
+\r
+\r
+    /**\r
+     * Current size of the cache in megapixels.\r
+     */\r
+    private int size = 0;\r
+    private int counter = 0;\r
+\r
+    class VolatileImageProviderImpl implements VolatileImageProvider {\r
+        private final int id;\r
+        private final int w;\r
+        private final int h;\r
+\r
+        private Reference<VolatileImage> imageRef = null;\r
+\r
+        public VolatileImageProviderImpl(int w, int h) {\r
+            this.id = counter++;\r
+            this.w = w;\r
+            this.h = h;\r
+        }\r
+\r
+        private VolatileImage dereferenceImage() {\r
+            return imageRef != null ? imageRef.get() : null;\r
+        }\r
+\r
+        public VolatileImage get(GraphicsConfiguration gc, AtomicInteger _validateResult) {\r
+            int validateResult = VolatileImage.IMAGE_INCOMPATIBLE;\r
+            VolatileImage vimg = dereferenceImage();\r
+            //System.out.println("GC: " + gc);\r
+            if (vimg != null)\r
+                validateResult = vimg.validate(gc);\r
+\r
+            if (validateResult == VolatileImage.IMAGE_RESTORED) {\r
+                if (DEBUG)\r
+                    System.out.println("VOLATILE IMAGE RESTORED for PROVIDER " + this);\r
+            }\r
+            if (validateResult == VolatileImage.IMAGE_INCOMPATIBLE) {\r
+                if (DEBUG)\r
+                    System.out.println("(RE)CREATING VOLATILE IMAGE FOR PROVIDER " + this);\r
+                vimg = gc.createCompatibleVolatileImage(w, h, Transparency.TRANSLUCENT);\r
+\r
+                if (vimg == null) {\r
+                    throw new IllegalStateException(this + ": got null VolatileImage from GraphicsConfiguration " + gc);\r
+                } else {\r
+                    this.imageRef = new SoftReference<VolatileImage>(vimg);\r
+\r
+                    // Implement move to front required for LRU\r
+                    synchronized (cache) {\r
+                        Object oldObject = cache.remove(this);\r
+                        boolean wasCached = oldObject == this;\r
+                        cache.put(this, this);\r
+                        if (!wasCached) {\r
+                            size += w * h;\r
+\r
+                            if (DEBUG)\r
+                                debug(this, cache.size(), size - w*h, size, "created new image");\r
+                        } else {\r
+                            //if (DEBUG)\r
+                            //    debug(this, cache.size(), size, size, "LRU move-to-front");\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (_validateResult != null)\r
+                _validateResult.set(validateResult);\r
+\r
+            return vimg;\r
+        }\r
+\r
+        public void dispose() {\r
+            int newSize;\r
+            synchronized (cache) {\r
+                newSize = size;\r
+                Object removed = cache.remove(this);\r
+                if (removed == this) {\r
+                    newSize -= w * h;\r
+                    size = newSize;\r
+\r
+                    if (DEBUG)\r
+                        debug(this, cache.size(), newSize + w*h, newSize, "explicitly disposed");\r
+                }\r
+                if (imageRef != null) {\r
+                    flushImageRef(imageRef);\r
+                    imageRef = null;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    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
+\r
+    private static void debug(VolatileImageProviderImpl i, int entries, int oldSize, int newSize, String msg) {\r
+        if (DEBUG) {\r
+            int sizeChange = newSize - oldSize;\r
+            String s = String.format(MSG, i.id, i.hashCode(), i.w, i.h, entries, oldSize, newSize, sizeChange, msg);\r
+            System.out.println(s);\r
+        }\r
+    }\r
+\r
+    private final Map<VolatileImageProviderImpl,VolatileImageProviderImpl> cache = new LinkedHashMap<VolatileImageProviderImpl, VolatileImageProviderImpl>(50, .75F, true) {\r
+        private static final long serialVersionUID = 5946026822169837291L;\r
+        @Override\r
+        protected boolean removeEldestEntry(Map.Entry<VolatileImageProviderImpl,VolatileImageProviderImpl> eldest) {\r
+            boolean result = size > MAX_SIZE;\r
+            if (result) {\r
+//                new Exception().printStackTrace();\r
+                VolatileImageProviderImpl impl = eldest.getKey();\r
+                size -= impl.w*impl.h;\r
+\r
+                //debug(impl, impl.imageId, cache.size(), oldSize, size, "cache full, dumping oldest");\r
+\r
+                flushImageRef(impl.imageRef);\r
+                impl.imageRef = null;\r
+            }\r
+            return result;\r
+        }\r
+    };\r
+\r
+    private static final void flushImageRef(Reference<? extends Image> imageRef) {\r
+        if (imageRef != null) {\r
+            Image img = imageRef.get();\r
+            imageRef.clear();\r
+            if (img != null)\r
+                img.flush();\r
+        }\r
+    }\r
+\r
+    VolatileImageProvider create(int w, int h) {\r
+//        System.out.println("VolatileImageProvider.create(" + w + ", " + h + ")");\r
+        VolatileImageProviderImpl result = new VolatileImageProviderImpl(w, h);\r
+        return result;\r
+    }\r
+\r
+}\r