1 /*
   2  * Copyright (c) 2003, 2008, 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.Color;
  29 import java.awt.Graphics;
  30 import java.awt.GraphicsConfiguration;
  31 import java.awt.GraphicsEnvironment;
  32 import java.awt.ImageCapabilities;
  33 import java.awt.image.BufferedImage;
  34 import java.awt.image.VolatileImage;
  35 import sun.awt.DisplayChangedListener;
  36 import sun.awt.image.SunVolatileImage;
  37 import sun.java2d.SunGraphicsEnvironment;
  38 import sun.java2d.SurfaceData;
  39 import sun.java2d.loops.CompositeType;
  40 import static sun.java2d.pipe.hw.AccelSurface.*;
  41 
  42 /**
  43  * This SurfaceManager variant manages an accelerated volatile surface, if it
  44  * is possible to create that surface.  If there is limited accelerated
  45  * memory, or if the volatile surface disappears due to an operating system
  46  * event, the VolatileSurfaceManager will attempt to restore the
  47  * accelerated surface.  If that fails, a system memory surface will be
  48  * created in its place.
  49  */
  50 public abstract class VolatileSurfaceManager
  51     extends SurfaceManager
  52     implements DisplayChangedListener
  53 {
  54     /**
  55      * A reference to the VolatileImage whose contents are being managed.
  56      */
  57     protected SunVolatileImage vImg;
  58 
  59     /**
  60      * The accelerated SurfaceData object.
  61      */
  62     protected SurfaceData sdAccel;
  63 
  64     /**
  65      * The software-based SurfaceData object.  Only create when first asked
  66      * to (otherwise it is a waste of memory as it will only be used in
  67      * situations of surface loss).
  68      */
  69     protected SurfaceData sdBackup;
  70 
  71     /**
  72      * The current SurfaceData object.
  73      */
  74     protected SurfaceData sdCurrent;
  75 
  76     /**
  77      * A record-keeping object.  This keeps track of which SurfaceData was
  78      * in use during the last call to validate().  This lets us see whether
  79      * the SurfaceData object has changed since then and allows us to return
  80      * the correct returnCode to the user in the validate() call.
  81      */
  82     protected SurfaceData sdPrevious;
  83 
  84     /**
  85      * Tracks loss of surface contents; queriable by user to see whether
  86      * contents need to be restored.
  87      */
  88     protected boolean lostSurface;
  89 
  90     /**
  91      * Context for extra initialization parameters.
  92      */
  93     protected Object context;
  94 
  95     protected VolatileSurfaceManager(SunVolatileImage vImg, Object context) {
  96         this.vImg = vImg;
  97         this.context = context;
  98 
  99         GraphicsEnvironment ge =
 100             GraphicsEnvironment.getLocalGraphicsEnvironment();
 101         // We could have a HeadlessGE at this point, so double-check before
 102         // assuming anything.
 103         if (ge instanceof SunGraphicsEnvironment) {
 104             ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this);
 105         }
 106     }
 107 
 108     /**
 109      * This init function is separate from the constructor because the
 110      * things we are doing here necessitate the object's existence.
 111      * Otherwise, we end up calling into a subclass' overridden method
 112      * during construction, before that subclass is completely constructed.
 113      */
 114     public void initialize() {
 115         if (isAccelerationEnabled()) {
 116             sdAccel = initAcceleratedSurface();
 117             if (sdAccel != null) {
 118                 sdCurrent = sdAccel;
 119             }
 120         }
 121         // only initialize the backup surface for images with unforced
 122         // acceleration type
 123         if (sdCurrent == null &&
 124             vImg.getForcedAccelSurfaceType() == UNDEFINED)
 125         {
 126             sdCurrent = getBackupSurface();
 127         }
 128     }
 129 
 130     public SurfaceData getPrimarySurfaceData() {
 131         return sdCurrent;
 132     }
 133 
 134     /**
 135      * Returns true if acceleration is enabled.  If not, we simply use the
 136      * backup SurfaceData object and return quickly from most methods
 137      * in this class.
 138      */
 139     protected abstract boolean isAccelerationEnabled();
 140 
 141     /**
 142      * Get the image ready for rendering.  This method is called to make
 143      * sure that the accelerated SurfaceData exists and is
 144      * ready to be used.  Users call this method prior to any set of
 145      * rendering to or from the image, to make sure the image is ready
 146      * and compatible with the given GraphicsConfiguration.
 147      *
 148      * The image may not be "ready" if either we had problems creating
 149      * it in the first place (e.g., there was no space in vram) or if
 150      * the surface became lost (e.g., some other app or the OS caused
 151      * vram surfaces to be removed).
 152      *
 153      * Note that we want to return RESTORED in any situation where the
 154      * SurfaceData is different than it was last time.  So whether it's
 155      * software or hardware, if we have a different SurfaceData object,
 156      * then the contents have been altered and we must reflect that
 157      * change to the user.
 158      */
 159     public int validate(GraphicsConfiguration gc) {
 160         int returnCode = VolatileImage.IMAGE_OK;
 161         boolean lostSurfaceTmp = lostSurface;
 162         lostSurface = false;
 163 
 164         if (isAccelerationEnabled()) {
 165             if (!isConfigValid(gc)) {
 166                 // If we're asked to render to a different device than the
 167                 // one we were created under, return INCOMPATIBLE error code.
 168                 // Note that a null gc simply ignores the incompatibility
 169                 // issue
 170                 returnCode = VolatileImage.IMAGE_INCOMPATIBLE;
 171             } else if (sdAccel == null) {
 172                 // We either had problems creating the surface or the display
 173                 // mode changed and we nullified the old one.  Try it again.
 174                 sdAccel = initAcceleratedSurface();
 175                 if (sdAccel != null) {
 176                     // set the current SurfaceData to accelerated version
 177                     sdCurrent = sdAccel;
 178                     // we don't need the system memory surface anymore, so
 179                     // let's release it now (it can always be restored later)
 180                     sdBackup = null;
 181                     returnCode = VolatileImage.IMAGE_RESTORED;
 182                 } else {
 183                     sdCurrent = getBackupSurface();
 184                 }
 185             } else if (sdAccel.isSurfaceLost()) {
 186                 try {
 187                     restoreAcceleratedSurface();
 188                     // set the current SurfaceData to accelerated version
 189                     sdCurrent = sdAccel;
 190                     // restoration successful: accel surface no longer lost
 191                     sdAccel.setSurfaceLost(false);
 192                     // we don't need the system memory surface anymore, so
 193                     // let's release it now (it can always be restored later)
 194                     sdBackup = null;
 195                     returnCode = VolatileImage.IMAGE_RESTORED;
 196                 } catch (sun.java2d.InvalidPipeException e) {
 197                     // Set the current SurfaceData to software version so that
 198                     // drawing can continue.  Note that we still have
 199                     // the lostAccelSurface flag set so that we will continue
 200                     // to attempt to restore the accelerated surface.
 201                     sdCurrent = getBackupSurface();
 202                 }
 203             } else if (lostSurfaceTmp) {
 204                 // Something else triggered this loss/restoration.  Could
 205                 // be a palette change that didn't require a SurfaceData
 206                 // recreation but merely a re-rendering of the pixels.
 207                 returnCode = VolatileImage.IMAGE_RESTORED;
 208             }
 209         } else if (sdAccel != null) {
 210             // if the "acceleration enabled" state changed to disabled,
 211             // switch to software surface
 212             sdCurrent = getBackupSurface();
 213             sdAccel = null;
 214             returnCode = VolatileImage.IMAGE_RESTORED;
 215         }
 216 
 217         if ((returnCode != VolatileImage.IMAGE_INCOMPATIBLE) &&
 218             (sdCurrent != sdPrevious))
 219         {
 220             // contents have changed - return RESTORED to user
 221             sdPrevious = sdCurrent;
 222             returnCode = VolatileImage.IMAGE_RESTORED;
 223         }
 224 
 225         if (returnCode == VolatileImage.IMAGE_RESTORED) {
 226             // clear the current surface with the background color,
 227             // only if the surface has been restored
 228             initContents();
 229         }
 230 
 231         return returnCode;
 232     }
 233 
 234     /**
 235      * Returns true if rendering data was lost since the last validate call.
 236      *
 237      * @see java.awt.image.VolatileImage#contentsLost
 238      */
 239     public boolean contentsLost() {
 240         return lostSurface;
 241     }
 242 
 243     /**
 244      * Creates a new accelerated surface that is compatible with the
 245      * current GraphicsConfiguration.  Returns the new accelerated
 246      * SurfaceData object, or null if the surface creation was not successful.
 247      *
 248      * Platform-specific subclasses should initialize an accelerated
 249      * surface (e.g. a DirectDraw surface on Windows, an OpenGL pbuffer,
 250      * or an X11 pixmap).
 251      */
 252     protected abstract SurfaceData initAcceleratedSurface();
 253 
 254     /**
 255      * Creates a software-based surface (of type BufImgSurfaceData).
 256      * The software representation is only created when needed, which
 257      * is only during some situation in which the hardware surface
 258      * cannot be allocated.  This allows apps to at least run,
 259      * albeit more slowly than they would otherwise.
 260      */
 261     protected SurfaceData getBackupSurface() {
 262         if (sdBackup == null) {
 263             BufferedImage bImg = vImg.getBackupImage();
 264             // Sabotage the acceleration capabilities of the BufImg surface
 265             SunWritableRaster.stealTrackable(bImg
 266                                              .getRaster()
 267                                              .getDataBuffer()).setUntrackable();
 268             sdBackup = BufImgSurfaceData.createData(bImg);
 269         }
 270         return sdBackup;
 271     }
 272 
 273     /**
 274      * Set contents of the current SurfaceData to default state (i.e. clear
 275      * the background).
 276      */
 277     public void initContents() {
 278         // images with forced acceleration type may have a null sdCurrent
 279         // because we do not create a backup surface for them
 280         if (sdCurrent != null) {
 281             Graphics g = vImg.createGraphics();
 282             g.clearRect(0, 0, vImg.getWidth(), vImg.getHeight());
 283             g.dispose();
 284         }
 285     }
 286 
 287     /**
 288      * Called from a SurfaceData object, indicating that our
 289      * accelerated surface has been lost and should be restored (perhaps
 290      * using a backup system memory surface).  Returns the newly restored
 291      * primary SurfaceData object.
 292      */
 293     public SurfaceData restoreContents() {
 294         return getBackupSurface();
 295     }
 296 
 297     /**
 298      * If the accelerated surface is the current SurfaceData for this manager,
 299      * sets the variable lostSurface to true, which indicates that something
 300      * happened to the image under management.  This variable is used in the
 301      * validate method to tell the caller that the surface contents need to
 302      * be restored.
 303      */
 304     public void acceleratedSurfaceLost() {
 305         if (isAccelerationEnabled() && (sdCurrent == sdAccel)) {
 306             lostSurface = true;
 307         }
 308     }
 309 
 310     /**
 311      * Restore sdAccel in case it was lost.  Do nothing in this
 312      * default case; platform-specific implementations may do more in
 313      * this situation as appropriate.
 314      */
 315     protected void restoreAcceleratedSurface() {
 316     }
 317 
 318     /**
 319      * Called from SunGraphicsEnv when there has been a display mode change.
 320      * Note that we simply invalidate hardware surfaces here; we do not
 321      * attempt to recreate or re-render them.  This is to avoid threading
 322      * conflicts with the native toolkit and associated threads.  Instead,
 323      * we just nullify the old surface data object and wait for a future
 324      * method in the rendering process to recreate the surface.
 325      */
 326     public void displayChanged() {
 327         if (!isAccelerationEnabled()) {
 328             return;
 329         }
 330         lostSurface = true;
 331         if (sdAccel != null) {
 332             // First, nullify the software surface.  This guards against
 333             // using a SurfaceData that was created in a different
 334             // display mode.
 335             sdBackup = null;
 336             sdCurrent = getBackupSurface();
 337             // Now, invalidate the old hardware-based SurfaceData
 338             SurfaceData oldData = sdAccel;
 339             sdAccel = null;
 340             oldData.invalidate();
 341         }
 342         // Update graphicsConfig for the vImg in case it changed due to
 343         // this display change event
 344         vImg.updateGraphicsConfig();
 345     }
 346 
 347     /**
 348      * When device palette changes, need to force a new copy
 349      * of the image into our hardware cache to update the
 350      * color indices of the pixels (indexed mode only).
 351      */
 352     public void paletteChanged() {
 353         lostSurface = true;
 354     }
 355 
 356     /**
 357      * Called by validate() to see whether the GC passed in is ok for
 358      * rendering to.  This generic implementation checks to see
 359      * whether the GC is either null or is from the same
 360      * device as the one that this image was created on.  Platform-
 361      * specific implementations may perform other checks as
 362      * appropriate.
 363      */
 364     protected boolean isConfigValid(GraphicsConfiguration gc) {
 365         return ((gc == null) ||
 366                 (gc.getDevice() == vImg.getGraphicsConfig().getDevice()));
 367     }
 368 
 369     @Override
 370     public ImageCapabilities getCapabilities(GraphicsConfiguration gc) {
 371         if (isConfigValid(gc)) {
 372             return isAccelerationEnabled() ?
 373                 new AcceleratedImageCapabilities() :
 374                 new ImageCapabilities(false);
 375         }
 376         return super.getCapabilities(gc);
 377     }
 378 
 379     private class AcceleratedImageCapabilities
 380         extends ImageCapabilities
 381     {
 382         AcceleratedImageCapabilities() {
 383             super(false);
 384         }
 385         @Override
 386         public boolean isAccelerated() {
 387             return (sdCurrent == sdAccel);
 388         }
 389         @Override
 390         public boolean isTrueVolatile() {
 391             return isAccelerated();
 392         }
 393     }
 394 
 395     /**
 396      * Releases any associated hardware memory for this image by
 397      * calling flush on sdAccel.  This method forces a lostSurface
 398      * situation so any future operations on the image will need to
 399      * revalidate the image first.
 400      */
 401     public void flush() {
 402         lostSurface = true;
 403         SurfaceData oldSD = sdAccel;
 404         sdAccel = null;
 405         if (oldSD != null) {
 406             oldSD.flush();
 407         }
 408     }
 409 }