changeset 2864:929a589c869f

RT-24516: simple cache for Images loaded from stylesheets.
author David Grieve<david.grieve@oracle.com>
date Fri, 08 Mar 2013 15:11:27 -0500
parents 811c33cc3302
children 199a494a54e0
files javafx-ui-common/src/com/sun/javafx/css/StyleManager.java javafx-ui-common/src/com/sun/javafx/css/converters/PaintConverter.java javafx-ui-common/src/javafx/scene/image/ImageView.java javafx-ui-common/src/javafx/scene/layout/BackgroundConverter.java javafx-ui-common/src/javafx/scene/layout/BorderConverter.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java javafx-ui-controls/src/javafx/scene/control/Labeled.java javafx-ui-controls/src/javafx/scene/control/Tooltip.java
diffstat 8 files changed, 103 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Fri Mar 08 15:11:27 2013 -0500
@@ -64,6 +64,8 @@
 import javafx.css.CssMetaData;
 import javafx.css.PseudoClass;
 import javafx.css.StyleOrigin;
+import javafx.scene.image.Image;
+import javafx.util.Pair;
 import sun.util.logging.PlatformLogger;
 
 /**
@@ -224,6 +226,10 @@
         // This list should also be fairly small
         final RefList<Key> keys;
         
+        // RT-24516 -- cache images coming from this stylesheet.
+        // This just holds a hard reference to the image. 
+        final List<Image> imageCache;
+        
         final int hash;
 
         StylesheetContainer(String fname, Stylesheet stylesheet) {
@@ -256,6 +262,9 @@
             this.sceneUsers = new RefList<Scene>();
             this.parentUsers = new RefList<Parent>();
             this.keys = new RefList<Key>();
+            
+            // this just holds a hard reference to the image
+            this.imageCache = new ArrayList<Image>();
         }
 
         @Override
@@ -398,7 +407,11 @@
     private void clearCache(StylesheetContainer sc) {
 
         removeFromCacheMap(sc);
-            
+        
+        // clean up image cache by removing images from the cache that 
+        // might have come from this stylesheet
+        cleanUpImageCache(sc.fname);
+                           
         final List<Reference<Scene>> sceneList = sc.sceneUsers.list;
         final List<Reference<Parent>> parentList = sc.parentUsers.list;
                         
@@ -479,6 +492,73 @@
         }
     }
     
+    ////////////////////////////////////////////////////////////////////////////
+    //
+    // Image caching
+    //
+    ////////////////////////////////////////////////////////////////////////////
+    
+    Map<String,Image> imageCache = new HashMap<String,Image>();
+    
+    public Image getCachedImage(String url) {
+    
+        Image image = imageCache.get(url);
+        if (image == null) {
+            
+            try {
+
+                image = new Image(url);
+                imageCache.put(url, image);
+                
+            } catch (IllegalArgumentException iae) {
+                // url was empty! 
+                final PlatformLogger logger = getLogger();
+                if (logger != null && logger.isLoggable(PlatformLogger.WARNING)) {
+                        LOGGER.warning(iae.getLocalizedMessage());
+                }
+
+            } catch (NullPointerException npe) {
+                // url was null!
+                final PlatformLogger logger = getLogger();
+                if (logger != null && logger.isLoggable(PlatformLogger.WARNING)) {
+                        LOGGER.warning(npe.getLocalizedMessage());
+                }
+            }
+        } 
+        
+        return image;
+    }
+    
+    private void cleanUpImageCache(String fname) {
+
+        if (fname == null && imageCache.isEmpty()) return;
+        if (fname.trim().isEmpty()) return;
+
+        int len = fname.lastIndexOf('/');
+        final String path = (len > 0) ? fname.substring(0,len) : fname;
+        final int plen = path.length();
+        
+        final String[] entriesToRemove = new String[imageCache.size()];
+        int count = 0;
+        
+        final Set<Entry<String, Image>> entrySet = imageCache.entrySet();
+        for(Entry<String, Image> entry : entrySet) {
+            
+            final String key = entry.getKey();
+            len = key.lastIndexOf('/');
+            final String kpath = (len > 0) ? key.substring(0, len) : key;
+            final int klen = kpath.length();
+            
+            // if the longer path begins with the shorter path,
+            // then assume the image came from this path.
+            boolean match = (klen > plen) ? kpath.startsWith(path) : path.startsWith(kpath);
+            if (match) entriesToRemove[count++] = key;
+        }
+        
+        for (int n=0; n<count; n++) {
+           Image img = imageCache.remove(entriesToRemove[n]);
+       }
+    }
     
     ////////////////////////////////////////////////////////////////////////////
     //
--- a/javafx-ui-common/src/com/sun/javafx/css/converters/PaintConverter.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-common/src/com/sun/javafx/css/converters/PaintConverter.java	Fri Mar 08 15:11:27 2013 -0500
@@ -38,6 +38,7 @@
 import com.sun.javafx.css.Size;
 import com.sun.javafx.css.SizeUnits;
 import com.sun.javafx.css.StyleConverterImpl;
+import com.sun.javafx.css.StyleManager;
 
 
 public final class PaintConverter extends StyleConverterImpl<ParsedValue<?, Paint>, Paint> {
@@ -152,7 +153,7 @@
             ParsedValue<?,?> urlParsedValue = values[0];
             String url = (String) urlParsedValue.convert(font);
             if (values.length == 1) {
-                return new ImagePattern(new Image(url));
+                return new ImagePattern(StyleManager.getInstance().getCachedImage(url));
             }
 
             Size x = (Size) values[1].convert(font);
--- a/javafx-ui-common/src/javafx/scene/image/ImageView.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-common/src/javafx/scene/image/ImageView.java	Fri Mar 08 15:11:27 2013 -0500
@@ -34,9 +34,11 @@
 import javafx.scene.Node;
 
 import com.sun.javafx.beans.event.AbstractNotifyListener;
+import com.sun.javafx.css.StyleManager;
 import javafx.css.CssMetaData;
 import javafx.css.StyleableStringProperty;
 import com.sun.javafx.css.converters.StringConverter;
+import com.sun.javafx.css.converters.URLConverter;
 import com.sun.javafx.geom.BaseBounds;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.jmx.MXNodeAlgorithm;
@@ -249,20 +251,9 @@
                 @Override
                 protected void invalidated() {
 
-                    String imageUrl = null;
-                    if (get() != null) {
-                        URL url = null;
-                        try {
-                            url = new URL(get());
-                        } catch (MalformedURLException malf) {
-                            // This may be a relative URL, so try resolving
-                            // it using the application classloader
-                            final ClassLoader cl = Thread.currentThread().getContextClassLoader();
-                            url = cl.getResource(get());
-                        }
-                        if (url != null) {
-                            setImage(new Image(url.toExternalForm())); 
-                        }
+                    final String imageUrl = get();
+                    if (imageUrl != null) {
+                        setImage(StyleManager.getInstance().getCachedImage(imageUrl)); 
                     } else {
                         setImage(null);
                     }                    
@@ -800,7 +791,7 @@
         // "preserve-ratio","smooth","viewport","fit-width","fit-height"
          private static final CssMetaData<ImageView, String> IMAGE = 
             new CssMetaData<ImageView,String>("-fx-image",
-                StringConverter.getInstance()) {
+                URLConverter.getInstance()) {
 
             @Override
             public boolean isSettable(ImageView n) {
--- a/javafx-ui-common/src/javafx/scene/layout/BackgroundConverter.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-common/src/javafx/scene/layout/BackgroundConverter.java	Fri Mar 08 15:11:27 2013 -0500
@@ -26,6 +26,7 @@
 package javafx.scene.layout;
 
 import com.sun.javafx.css.StyleConverterImpl;
+import com.sun.javafx.css.StyleManager;
 import com.sun.javafx.scene.layout.region.RepeatStruct;
 import java.util.Map;
 import javafx.css.CssMetaData;
@@ -107,7 +108,7 @@
                 // RT-21335: skip background and border images whose image url is null
                 if (imageUrls[i] == null) continue;
 
-                final Image image = new Image(imageUrls[i]);
+                final Image image = StyleManager.getInstance().getCachedImage(imageUrls[i]);
                 final RepeatStruct repeat = (repeats.length > 0) ?
                         repeats[i <= lastRepeatIndex ? i : lastRepeatIndex] : null; // min
                 final BackgroundPosition position = (positions.length > 0) ?
--- a/javafx-ui-common/src/javafx/scene/layout/BorderConverter.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-common/src/javafx/scene/layout/BorderConverter.java	Fri Mar 08 15:11:27 2013 -0500
@@ -26,6 +26,7 @@
 package javafx.scene.layout;
 
 import com.sun.javafx.css.StyleConverterImpl;
+import com.sun.javafx.css.StyleManager;
 import com.sun.javafx.scene.layout.region.BorderImageSlices;
 import com.sun.javafx.scene.layout.region.Margins;
 import com.sun.javafx.scene.layout.region.RepeatStruct;
@@ -163,7 +164,8 @@
                 final BorderImageSlices slice = slices.length > 0 ? slices[i <= lastSlicesIndex ? i : lastSlicesIndex] : BorderImageSlices.EMPTY;
                 final Insets inset = insets.length > 0 ? insets[i <= lastInsetsIndex ? i : lastInsetsIndex] : Insets.EMPTY;
                 final BorderWidths width = widths.length > 0 ? widths[i <= lastWidthsIndex ? i : lastWidthsIndex] : BorderWidths.DEFAULT;
-                borderImages[i] = new BorderImage(new Image(imageUrls[i]), width, inset, slice.widths, slice.filled, repeatX, repeatY);
+                final Image img = StyleManager.getInstance().getCachedImage(imageUrls[i]);
+                borderImages[i] = new BorderImage(img, width, inset, slice.widths, slice.filled, repeatX, repeatY);
             }
         }
 
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java	Fri Mar 08 15:11:27 2013 -0500
@@ -25,6 +25,7 @@
 
 package com.sun.javafx.scene.control.skin;
 
+import com.sun.javafx.css.StyleManager;
 import javafx.beans.property.StringProperty;
 import javafx.css.StyleOrigin;
 import javafx.css.StyleableBooleanProperty;
@@ -96,9 +97,9 @@
             } else {
                 if (pickerColorBox.getChildren().size() == 2) {
                     ImageView imageView = (ImageView)pickerColorBox.getChildren().get(1);
-                    imageView.setImage(new Image(v));
+                    imageView.setImage(StyleManager.getInstance().getCachedImage(v));
                 } else {
-                    pickerColorBox.getChildren().add(new ImageView(new Image(v)));
+                    pickerColorBox.getChildren().add(new ImageView(StyleManager.getInstance().getCachedImage(v)));
                 }
             }
         }
--- a/javafx-ui-controls/src/javafx/scene/control/Labeled.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-controls/src/javafx/scene/control/Labeled.java	Fri Mar 08 15:11:27 2013 -0500
@@ -28,6 +28,7 @@
 
 import com.sun.javafx.css.Selector;
 import com.sun.javafx.css.SimpleSelector;
+import com.sun.javafx.css.StyleManager;
 import com.sun.javafx.css.converters.BooleanConverter;
 import com.sun.javafx.css.converters.EnumConverter;
 import com.sun.javafx.css.converters.InsetsConverter;
@@ -446,7 +447,8 @@
                             url = cl.getResource(v);
                         }
                         if (url != null) {
-                            ((StyleableProperty)graphicProperty()).applyStyle(origin, new ImageView(new Image(url.toExternalForm())));
+                            final Image img = StyleManager.getInstance().getCachedImage(url.toExternalForm());
+                            ((StyleableProperty)graphicProperty()).applyStyle(origin, new ImageView(img));
                         }
                     }
                 }
--- a/javafx-ui-controls/src/javafx/scene/control/Tooltip.java	Fri Mar 08 10:44:56 2013 -0500
+++ b/javafx-ui-controls/src/javafx/scene/control/Tooltip.java	Fri Mar 08 15:11:27 2013 -0500
@@ -26,6 +26,7 @@
 package javafx.scene.control;
 
 
+import com.sun.javafx.css.StyleManager;
 import javafx.css.StyleableBooleanProperty;
 import javafx.css.StyleableDoubleProperty;
 import javafx.css.StyleableObjectProperty;
@@ -596,7 +597,8 @@
                                 url = cl.getResource(get());
                             }
                             if (url != null) {
-                                setGraphic(new ImageView(new Image(url.toExternalForm())));                            
+                                final Image img = StyleManager.getInstance().getCachedImage(url.toExternalForm());
+                                setGraphic(new ImageView(img));                            
                             }
                         } else {
                             setGraphic(null);