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 // Note that getBackupSurface may set sdAccel to null!! 339 if (sdAccel != null) { 340 SurfaceData oldData = sdAccel; 341 sdAccel = null; 342 oldData.invalidate(); 343 } 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 }