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 }