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