1 /*
   2  * Copyright (c) 2003, 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.GraphicsEnvironment;
  29 import java.awt.GraphicsConfiguration;
  30 import java.awt.Image;
  31 import java.awt.ImageCapabilities;
  32 import java.awt.image.BufferedImage;
  33 import java.awt.image.VolatileImage;
  34 import java.util.concurrent.ConcurrentHashMap;
  35 import java.util.Iterator;
  36 import sun.java2d.SurfaceData;
  37 import sun.java2d.SurfaceDataProxy;
  38 
  39 /**
  40  * The abstract base class that manages the various SurfaceData objects that
  41  * represent an Image's contents.  Subclasses can customize how the surfaces
  42  * are organized, whether to cache the original contents in an accelerated
  43  * surface, and so on.
  44  * <p>
  45  * The SurfaceManager also maintains an arbitrary "cache" mechanism which
  46  * allows other agents to store data in it specific to their use of this
  47  * image.  The most common use of the caching mechanism is for destination
  48  * SurfaceData objects to store cached copies of the source image.
  49  */
  50 public abstract class SurfaceManager {
  51 
  52     public static abstract class ImageAccessor {
  53         public abstract SurfaceManager getSurfaceManager(Image img);
  54         public abstract void setSurfaceManager(Image img, SurfaceManager mgr);
  55     }
  56 
  57     private static ImageAccessor imgaccessor;
  58 
  59     public static void setImageAccessor(ImageAccessor ia) {
  60         if (imgaccessor != null) {
  61             throw new InternalError("Attempt to set ImageAccessor twice");
  62         }
  63         imgaccessor = ia;
  64     }
  65     
  66     /**
  67      * Returns the SurfaceManager object contained within the given Image.
  68      */
  69     public static SurfaceManager getManager(Image img) {
  70         SurfaceManager sMgr = imgaccessor.getSurfaceManager(img);
  71         if (sMgr == null) {
  72             /*
  73              * In practice only a BufferedImage will get here.
  74              */
  75             try {
  76                 BufferedImage bi = (BufferedImage) img;
  77                 sMgr = new BufImgSurfaceManager(bi);
  78                 setManager(bi, sMgr);
  79             } catch (ClassCastException e) {
  80                 throw new IllegalArgumentException("Invalid Image variant");
  81             }
  82         }
  83         return sMgr;
  84     }
  85 
  86     public static void setManager(Image img, SurfaceManager mgr) {
  87         imgaccessor.setSurfaceManager(img, mgr);
  88     }
  89 
  90     private ConcurrentHashMap<Object,Object> cacheMap;
  91 
  92     /**
  93      * Return an arbitrary cached object for an arbitrary cache key.
  94      * Other objects can use this mechanism to store cached data about
  95      * the source image that will let them save time when using or
  96      * manipulating the image in the future.
  97      * <p>
  98      * Note that the cache is maintained as a simple Map with no
  99      * attempts to keep it up to date or invalidate it so any data
 100      * stored here must either not be dependent on the state of the
 101      * image or it must be individually tracked to see if it is
 102      * outdated or obsolete.
 103      * <p>
 104      * The SurfaceData object of the primary (destination) surface
 105      * has a StateTracker mechanism which can help track the validity
 106      * and "currentness" of any data stored here.
 107      * For convenience and expediency an object stored as cached
 108      * data may implement the FlushableCacheData interface specified
 109      * below so that it may be notified immediately if the flush()
 110      * method is ever called.
 111      */
 112     public Object getCacheData(Object key) {
 113         return (cacheMap == null) ? null : cacheMap.get(key);
 114     }
 115 
 116     /**
 117      * Store an arbitrary cached object for an arbitrary cache key.
 118      * See the getCacheData() method for notes on tracking the
 119      * validity of data stored using this mechanism.
 120      */
 121     public void setCacheData(Object key, Object value) {
 122         if (cacheMap == null) {
 123             synchronized (this) {
 124                 if (cacheMap == null) {
 125                     cacheMap = new ConcurrentHashMap<>(2);
 126                 }
 127             }
 128         }
 129         cacheMap.put(key, value);
 130     }
 131 
 132     /**
 133      * Returns the main SurfaceData object that "owns" the pixels for
 134      * this SurfaceManager.  This SurfaceData is used as the destination
 135      * surface in a rendering operation and is the most authoritative
 136      * storage for the current state of the pixels, though other
 137      * versions might be cached in other locations for efficiency.
 138      */
 139     public abstract SurfaceData getPrimarySurfaceData();
 140 
 141     /**
 142      * Restores the primary surface being managed, and then returns the
 143      * replacement surface.  This is called when an accelerated surface has
 144      * been "lost", in an attempt to auto-restore its contents.
 145      */
 146     public abstract SurfaceData restoreContents();
 147 
 148     /**
 149      * Notification that any accelerated surfaces associated with this manager
 150      * have been "lost", which might mean that they need to be manually
 151      * restored or recreated.
 152      *
 153      * The default implementation does nothing, but platform-specific
 154      * variants which have accelerated surfaces should perform any necessary
 155      * actions.
 156      */
 157     public void acceleratedSurfaceLost() {}
 158 
 159     /**
 160      * Returns an ImageCapabilities object which can be
 161      * inquired as to the specific capabilities of this
 162      * Image.  The capabilities object will return true for
 163      * isAccelerated() if the image has a current and valid
 164      * SurfaceDataProxy object cached for the specified
 165      * GraphicsConfiguration parameter.
 166      * <p>
 167      * This class provides a default implementation of the
 168      * ImageCapabilities that will try to determine if there
 169      * is an associated SurfaceDataProxy object and if it is
 170      * up to date, but only works for GraphicsConfiguration
 171      * objects which implement the ProxiedGraphicsConfig
 172      * interface defined below.  In practice, all configs
 173      * which can be accelerated are currently implementing
 174      * that interface.
 175      * <p>
 176      * A null GraphicsConfiguration returns a value based on whether the
 177      * image is currently accelerated on its default GraphicsConfiguration.
 178      *
 179      * @see java.awt.Image#getCapabilities
 180      * @since 1.5
 181      */
 182     public ImageCapabilities getCapabilities(GraphicsConfiguration gc) {
 183         return new ImageCapabilitiesGc(gc);
 184     }
 185 
 186     class ImageCapabilitiesGc extends ImageCapabilities {
 187         GraphicsConfiguration gc;
 188 
 189         public ImageCapabilitiesGc(GraphicsConfiguration gc) {
 190             super(false);
 191             this.gc = gc;
 192         }
 193 
 194         public boolean isAccelerated() {
 195             // Note that when img.getAccelerationPriority() gets set to 0
 196             // we remove SurfaceDataProxy objects from the cache and the
 197             // answer will be false.
 198             GraphicsConfiguration tmpGc = gc;
 199             if (tmpGc == null) {
 200                 tmpGc = GraphicsEnvironment.getLocalGraphicsEnvironment().
 201                     getDefaultScreenDevice().getDefaultConfiguration();
 202             }
 203             if (tmpGc instanceof ProxiedGraphicsConfig) {
 204                 Object proxyKey =
 205                     ((ProxiedGraphicsConfig) tmpGc).getProxyKey();
 206                 if (proxyKey != null) {
 207                     SurfaceDataProxy sdp =
 208                         (SurfaceDataProxy) getCacheData(proxyKey);
 209                     return (sdp != null && sdp.isAccelerated());
 210                 }
 211             }
 212             return false;
 213         }
 214     }
 215 
 216     /**
 217      * An interface for GraphicsConfiguration objects to implement if
 218      * their surfaces accelerate images using SurfaceDataProxy objects.
 219      *
 220      * Implementing this interface facilitates the default
 221      * implementation of getImageCapabilities() above.
 222      */
 223     public static interface ProxiedGraphicsConfig {
 224         /**
 225          * Return the key that destination surfaces created on the
 226          * given GraphicsConfiguration use to store SurfaceDataProxy
 227          * objects for their cached copies.
 228          */
 229         public Object getProxyKey();
 230     }
 231 
 232     /**
 233      * Releases system resources in use by ancillary SurfaceData objects,
 234      * such as surfaces cached in accelerated memory.  Subclasses should
 235      * override to release any of their flushable data.
 236      * <p>
 237      * The default implementation will visit all of the value objects
 238      * in the cacheMap and flush them if they implement the
 239      * FlushableCacheData interface.
 240      */
 241     public synchronized void flush() {
 242         flush(false);
 243     }
 244 
 245     synchronized void flush(boolean deaccelerate) {
 246         if (cacheMap != null) {
 247             Iterator<Object> i = cacheMap.values().iterator();
 248             while (i.hasNext()) {
 249                 Object o = i.next();
 250                 if (o instanceof FlushableCacheData) {
 251                     if (((FlushableCacheData) o).flush(deaccelerate)) {
 252                         i.remove();
 253                     }
 254                 }
 255             }
 256         }
 257     }
 258 
 259     /**
 260      * An interface for Objects used in the SurfaceManager cache
 261      * to implement if they have data that should be flushed when
 262      * the Image is flushed.
 263      */
 264     public static interface FlushableCacheData {
 265         /**
 266          * Flush all cached resources.
 267          * The deaccelerated parameter indicates if the flush is
 268          * happening because the associated surface is no longer
 269          * being accelerated (for instance the acceleration priority
 270          * is set below the threshold needed for acceleration).
 271          * Returns a boolean that indicates if the cached object is
 272          * no longer needed and should be removed from the cache.
 273          */
 274         public boolean flush(boolean deaccelerated);
 275     }
 276 
 277     /**
 278      * Called when image's acceleration priority is changed.
 279      * <p>
 280      * The default implementation will visit all of the value objects
 281      * in the cacheMap when the priority gets set to 0.0 and flush them
 282      * if they implement the FlushableCacheData interface.
 283      */
 284     public void setAccelerationPriority(float priority) {
 285         if (priority == 0.0f) {
 286             flush(true);
 287         }
 288     }
 289 
 290     /**
 291      * Returns a scale factor of the image. This is utility method, which
 292      * fetches information from the SurfaceData of the image.
 293      *
 294      * @see SurfaceData#getDefaultScale
 295      */
 296     public static int getImageScale(final Image img) {
 297         if (!(img instanceof VolatileImage) &&
 298             !(img instanceof OffScreenImage))
 299         {
 300             return 1;
 301         }
 302         final SurfaceManager sm = getManager(img);
 303         return sm.getPrimarySurfaceData().getDefaultScale();
 304     }
 305 }