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