1 /* 2 * Copyright (c) 2007, 2013, 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.java2d; 27 28 import java.awt.Color; 29 import java.awt.Rectangle; 30 import java.awt.AlphaComposite; 31 import java.awt.GraphicsEnvironment; 32 33 import sun.awt.DisplayChangedListener; 34 import sun.java2d.StateTrackable.State; 35 import sun.java2d.loops.CompositeType; 36 import sun.java2d.loops.SurfaceType; 37 import sun.java2d.loops.Blit; 38 import sun.java2d.loops.BlitBg; 39 import sun.awt.image.SurfaceManager; 40 import sun.awt.image.SurfaceManager.FlushableCacheData; 41 42 import java.security.AccessController; 43 import sun.security.action.GetPropertyAction; 44 45 /** 46 * The proxy class encapsulates the logic for managing alternate 47 * SurfaceData representations of a primary SurfaceData. 48 * The main class will handle tracking the state changes of the 49 * primary SurfaceData and updating the associated SurfaceData 50 * proxy variants. 51 * <p> 52 * Subclasses have 2 main responsibilities: 53 * <ul> 54 * <li> Override the isSupportedOperation() method to determine if 55 * a given operation can be accelerated with a given source 56 * SurfaceData 57 * <li> Override the validateSurfaceData() method to create or update 58 * a given accelerated surface to hold the pixels for the indicated 59 * source SurfaceData 60 * </ul> 61 * If necessary, a subclass may also override the updateSurfaceData 62 * method to transfer the pixels to the accelerated surface. 63 * By default the parent class will transfer the pixels using a 64 * standard Blit operation between the two SurfaceData objects. 65 */ 66 public abstract class SurfaceDataProxy 67 implements DisplayChangedListener, SurfaceManager.FlushableCacheData 68 { 69 private static boolean cachingAllowed; 70 private static int defaultThreshold; 71 72 static { 73 cachingAllowed = true; 74 String manimg = AccessController.doPrivileged( 75 new GetPropertyAction("sun.java2d.managedimages")); 76 if (manimg != null && manimg.equals("false")) { 77 cachingAllowed = false; 78 System.out.println("Disabling managed images"); 79 } 80 81 defaultThreshold = 1; 82 String num = AccessController.doPrivileged( 83 new GetPropertyAction("sun.java2d.accthreshold")); 84 if (num != null) { 85 try { 86 int parsed = Integer.parseInt(num); 87 if (parsed >= 0) { 88 defaultThreshold = parsed; 89 System.out.println("New Default Acceleration Threshold: " + 90 defaultThreshold); 91 } 92 } catch (NumberFormatException e) { 93 System.err.println("Error setting new threshold:" + e); 94 } 95 } 96 } 97 isCachingAllowed()98 public static boolean isCachingAllowed() { 99 return cachingAllowed; 100 } 101 102 /** 103 * Determine if an alternate form for the srcData is needed 104 * and appropriate from the given operational parameters. 105 */ isSupportedOperation(SurfaceData srcData, int txtype, CompositeType comp, Color bgColor)106 public abstract boolean isSupportedOperation(SurfaceData srcData, 107 int txtype, 108 CompositeType comp, 109 Color bgColor); 110 111 /** 112 * Construct an alternate form of the given SurfaceData. 113 * The contents of the returned SurfaceData may be undefined 114 * since the calling code will take care of updating the 115 * contents with a subsequent call to updateSurfaceData. 116 * <p> 117 * If the method returns null then there was a problem with 118 * allocating the accelerated surface. The getRetryTracker() 119 * method will be called to track when to attempt another 120 * revalidation. 121 */ validateSurfaceData(SurfaceData srcData, SurfaceData cachedData, int w, int h)122 public abstract SurfaceData validateSurfaceData(SurfaceData srcData, 123 SurfaceData cachedData, 124 int w, int h); 125 126 /** 127 * If the subclass is unable to validate or create a cached 128 * SurfaceData then this method will be used to get a 129 * StateTracker object that will indicate when to attempt 130 * to validate the surface again. Subclasses may return 131 * trackers which count down an ever increasing threshold 132 * to provide hysteresis on creating surfaces during low 133 * memory conditions. The default implementation just waits 134 * another "threshold" number of accesses before trying again. 135 */ getRetryTracker(SurfaceData srcData)136 public StateTracker getRetryTracker(SurfaceData srcData) { 137 return new CountdownTracker(threshold); 138 } 139 140 public static class CountdownTracker implements StateTracker { 141 private int countdown; 142 CountdownTracker(int threshold)143 public CountdownTracker(int threshold) { 144 this.countdown = threshold; 145 } 146 isCurrent()147 public synchronized boolean isCurrent() { 148 return (--countdown >= 0); 149 } 150 } 151 152 /** 153 * This instance is for cases where a caching implementation 154 * determines that a particular source image will never need 155 * to be cached - either the source SurfaceData was of an 156 * incompatible type, or it was in an UNTRACKABLE state or 157 * some other factor is discovered that permanently prevents 158 * acceleration or caching. 159 * This class optimally implements NOP variants of all necessary 160 * methods to avoid caching with a minimum of fuss. 161 */ 162 public static SurfaceDataProxy UNCACHED = new SurfaceDataProxy(0) { 163 @Override 164 public boolean isAccelerated() { 165 return false; 166 } 167 168 @Override 169 public boolean isSupportedOperation(SurfaceData srcData, 170 int txtype, 171 CompositeType comp, 172 Color bgColor) 173 { 174 return false; 175 } 176 177 @Override 178 public SurfaceData validateSurfaceData(SurfaceData srcData, 179 SurfaceData cachedData, 180 int w, int h) 181 { 182 throw new InternalError("UNCACHED should never validate SDs"); 183 } 184 185 @Override 186 public SurfaceData replaceData(SurfaceData srcData, 187 int txtype, 188 CompositeType comp, 189 Color bgColor) 190 { 191 // Not necessary to override this, but doing so is faster 192 return srcData; 193 } 194 }; 195 196 // The number of attempts to copy from a STABLE source before 197 // a cached copy is created or updated. 198 private int threshold; 199 200 /* 201 * Source tracking data 202 * 203 * Every time that srcTracker is out of date we will reset numtries 204 * to threshold and set the cacheTracker to one that is non-current. 205 * numtries will then count down to 0 at which point the cacheTracker 206 * will remind us that we need to update the cachedSD before we can 207 * use it. 208 * 209 * Note that since these fields interrelate we should synchronize 210 * whenever we update them, but it should be OK to read them 211 * without synchronization. 212 */ 213 private StateTracker srcTracker; 214 private int numtries; 215 216 /* 217 * Cached data 218 * 219 * We cache a SurfaceData created by the subclass in cachedSD and 220 * track its state (isValid and !surfaceLost) in cacheTracker. 221 * 222 * Also, when we want to note that cachedSD needs to be updated 223 * we replace the cacheTracker with a NEVER_CURRENT tracker which 224 * will cause us to try to revalidate and update the surface on 225 * next use. 226 */ 227 private SurfaceData cachedSD; 228 private StateTracker cacheTracker; 229 230 /* 231 * Are we still the best object to control caching of data 232 * for the source image? 233 */ 234 private boolean valid; 235 236 /** 237 * Create a SurfaceData proxy manager that attempts to create 238 * and cache a variant copy of the source SurfaceData after 239 * the default threshold number of attempts to copy from the 240 * STABLE source. 241 */ SurfaceDataProxy()242 public SurfaceDataProxy() { 243 this(defaultThreshold); 244 } 245 246 /** 247 * Create a SurfaceData proxy manager that attempts to create 248 * and cache a variant copy of the source SurfaceData after 249 * the specified threshold number of attempts to copy from 250 * the STABLE source. 251 */ SurfaceDataProxy(int threshold)252 public SurfaceDataProxy(int threshold) { 253 this.threshold = threshold; 254 255 this.srcTracker = StateTracker.NEVER_CURRENT; 256 // numtries will be reset on first use 257 this.cacheTracker = StateTracker.NEVER_CURRENT; 258 259 this.valid = true; 260 } 261 262 /** 263 * Returns true iff this SurfaceData proxy is still the best 264 * way to control caching of the given source on the given 265 * destination. 266 */ isValid()267 public boolean isValid() { 268 return valid; 269 } 270 271 /** 272 * Sets the valid state to false so that the next time this 273 * proxy is fetched to generate a replacement SurfaceData, 274 * the code in SurfaceData knows to replace the proxy first. 275 */ invalidate()276 public void invalidate() { 277 this.valid = false; 278 } 279 280 /** 281 * Flush all cached resources as per the FlushableCacheData interface. 282 * The deaccelerated parameter indicates if the flush is 283 * happening because the associated surface is no longer 284 * being accelerated (for instance the acceleration priority 285 * is set below the threshold needed for acceleration). 286 * Returns a boolean that indicates if the cached object is 287 * no longer needed and should be removed from the cache. 288 */ flush(boolean deaccelerated)289 public boolean flush(boolean deaccelerated) { 290 if (deaccelerated) { 291 invalidate(); 292 } 293 flush(); 294 return !isValid(); 295 } 296 297 /** 298 * Actively flushes (drops and invalidates) the cached surface 299 * so that it can be reclaimed quickly. 300 */ flush()301 public synchronized void flush() { 302 SurfaceData csd = this.cachedSD; 303 this.cachedSD = null; 304 this.cacheTracker = StateTracker.NEVER_CURRENT; 305 if (csd != null) { 306 csd.flush(); 307 } 308 } 309 310 /** 311 * Returns true iff this SurfaceData proxy is still valid 312 * and if it has a currently cached replacement that is also 313 * valid and current. 314 */ isAccelerated()315 public boolean isAccelerated() { 316 return (isValid() && 317 srcTracker.isCurrent() && 318 cacheTracker.isCurrent()); 319 } 320 321 /** 322 * This method should be called from subclasses which create 323 * cached SurfaceData objects that depend on the current 324 * properties of the display. 325 */ activateDisplayListener()326 protected void activateDisplayListener() { 327 GraphicsEnvironment ge = 328 GraphicsEnvironment.getLocalGraphicsEnvironment(); 329 // We could have a HeadlessGE at this point, so double-check before 330 // assuming anything. 331 // Also, no point in listening to display change events if 332 // the image is never going to be accelerated. 333 if (ge instanceof SunGraphicsEnvironment) { 334 ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this); 335 } 336 } 337 338 /** 339 * Invoked when the display mode has changed. 340 * This method will invalidate and drop the internal cachedSD object. 341 */ displayChanged()342 public void displayChanged() { 343 flush(); 344 } 345 346 /** 347 * Invoked when the palette has changed. 348 */ paletteChanged()349 public void paletteChanged() { 350 // We could potentially get away with just resetting cacheTracker 351 // here but there is a small window of vulnerability in the 352 // replaceData method where we could be just finished with 353 // updating the cachedSD when this method is called and even 354 // though we set a non-current cacheTracker here it will then 355 // immediately get set to a current one by the thread that is 356 // updating the cachedSD. It is safer to just replace the 357 // srcTracker with a non-current version that will trigger a 358 // full update cycle the next time this proxy is used. 359 // The downside is having to go through a full threshold count 360 // before we can update and use our cache again, but palette 361 // changes should be relatively rare... 362 this.srcTracker = StateTracker.NEVER_CURRENT; 363 } 364 365 /** 366 * This method attempts to replace the srcData with a cached version. 367 * It relies on the subclass to determine if the cached version will 368 * be useful given the operational parameters. 369 * This method checks any preexisting cached copy for being "up to date" 370 * and tries to update it if it is stale or non-existant and the 371 * appropriate number of accesses have occurred since it last was stale. 372 * <p> 373 * An outline of the process is as follows: 374 * <ol> 375 * <li> Check the operational parameters (txtype, comp, bgColor) 376 * to make sure that the operation is supported. Return the 377 * original SurfaceData if the operation cannot be accelerated. 378 * <li> Check the tracker for the source surface to see if it has 379 * remained stable since it was last cached. Update the state 380 * variables to cause both a threshold countdown and an update 381 * of the cached copy if it is not. (Setting cacheTracker to 382 * NEVER_CURRENT effectively marks it as "needing to be updated".) 383 * <li> Check the tracker for the cached copy to see if is still 384 * valid and up to date. Note that the cacheTracker may be 385 * non-current if either something happened to the cached copy 386 * (eg. surfaceLost) or if the source was out of date and the 387 * cacheTracker was set to NEVER_CURRENT to force an update. 388 * Decrement the countdown and copy the source to the cache 389 * as necessary and then update the variables to show that 390 * the cached copy is stable. 391 * </ol> 392 */ replaceData(SurfaceData srcData, int txtype, CompositeType comp, Color bgColor)393 public SurfaceData replaceData(SurfaceData srcData, 394 int txtype, 395 CompositeType comp, 396 Color bgColor) 397 { 398 if (isSupportedOperation(srcData, txtype, comp, bgColor)) { 399 // First deal with tracking the source. 400 if (!srcTracker.isCurrent()) { 401 synchronized (this) { 402 this.numtries = threshold; 403 this.srcTracker = srcData.getStateTracker(); 404 this.cacheTracker = StateTracker.NEVER_CURRENT; 405 } 406 407 if (!srcTracker.isCurrent()) { 408 // Dynamic or Untrackable (or a very recent modification) 409 if (srcData.getState() == State.UNTRACKABLE) { 410 // UNTRACKABLE means we can never cache again. 411 412 // Invalidate so we get replaced next time we are used 413 // (presumably with an UNCACHED proxy). 414 invalidate(); 415 416 // Aggressively drop our reference to the cachedSD 417 // in case this proxy is not consulted again (and 418 // thus replaced) for a long time. 419 flush(); 420 } 421 return srcData; 422 } 423 } 424 425 // Then deal with checking the validity of the cached SurfaceData 426 SurfaceData csd = this.cachedSD; 427 if (!cacheTracker.isCurrent()) { 428 // Next make sure the dust has settled 429 synchronized (this) { 430 if (numtries > 0) { 431 --numtries; 432 return srcData; 433 } 434 } 435 436 Rectangle r = srcData.getBounds(); 437 int w = r.width; 438 int h = r.height; 439 440 // Snapshot the tracker in case it changes while 441 // we are updating the cached SD... 442 StateTracker curTracker = srcTracker; 443 444 csd = validateSurfaceData(srcData, csd, w, h); 445 if (csd == null) { 446 synchronized (this) { 447 if (curTracker == srcTracker) { 448 this.cacheTracker = getRetryTracker(srcData); 449 this.cachedSD = null; 450 } 451 } 452 return srcData; 453 } 454 455 updateSurfaceData(srcData, csd, w, h); 456 if (!csd.isValid()) { 457 return srcData; 458 } 459 460 synchronized (this) { 461 // We only reset these variables if the tracker from 462 // before the surface update is still in use and current 463 // Note that we must use a srcTracker that was fetched 464 // from before the update process to make sure that we 465 // do not lose some pixel changes in the shuffle. 466 if (curTracker == srcTracker && curTracker.isCurrent()) { 467 this.cacheTracker = csd.getStateTracker(); 468 this.cachedSD = csd; 469 } 470 } 471 } 472 473 if (csd != null) { 474 return csd; 475 } 476 } 477 478 return srcData; 479 } 480 481 /** 482 * This is the default implementation for updating the cached 483 * SurfaceData from the source (primary) SurfaceData. 484 * A simple Blit is used to copy the pixels from the source to 485 * the destination SurfaceData. 486 * A subclass can override this implementation if a more complex 487 * operation is required to update its cached copies. 488 */ updateSurfaceData(SurfaceData srcData, SurfaceData dstData, int w, int h)489 public void updateSurfaceData(SurfaceData srcData, 490 SurfaceData dstData, 491 int w, int h) 492 { 493 SurfaceType srcType = srcData.getSurfaceType(); 494 SurfaceType dstType = dstData.getSurfaceType(); 495 Blit blit = Blit.getFromCache(srcType, 496 CompositeType.SrcNoEa, 497 dstType); 498 blit.Blit(srcData, dstData, 499 AlphaComposite.Src, null, 500 0, 0, 0, 0, w, h); 501 dstData.markDirty(); 502 } 503 504 /** 505 * This is an alternate implementation for updating the cached 506 * SurfaceData from the source (primary) SurfaceData using a 507 * background color for transparent pixels. 508 * A simple BlitBg is used to copy the pixels from the source to 509 * the destination SurfaceData with the specified bgColor. 510 * A subclass can override the normal updateSurfaceData method 511 * and call this implementation instead if it wants to use color 512 * keying for bitmask images. 513 */ updateSurfaceDataBg(SurfaceData srcData, SurfaceData dstData, int w, int h, Color bgColor)514 public void updateSurfaceDataBg(SurfaceData srcData, 515 SurfaceData dstData, 516 int w, int h, Color bgColor) 517 { 518 SurfaceType srcType = srcData.getSurfaceType(); 519 SurfaceType dstType = dstData.getSurfaceType(); 520 BlitBg blitbg = BlitBg.getFromCache(srcType, 521 CompositeType.SrcNoEa, 522 dstType); 523 blitbg.BlitBg(srcData, dstData, 524 AlphaComposite.Src, null, bgColor.getRGB(), 525 0, 0, 0, 0, w, h); 526 dstData.markDirty(); 527 } 528 } 529