1 /* 2 * Copyright (c) 2005, 2011, 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 package javax.swing; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.awt.image.*; 30 import java.lang.reflect.*; 31 import java.lang.ref.WeakReference; 32 import java.util.*; 33 34 import com.sun.java.swing.SwingUtilities3; 35 36 import sun.awt.SubRegionShowable; 37 import sun.java2d.SunGraphics2D; 38 import sun.java2d.pipe.hw.ExtendedBufferCapabilities; 39 import sun.awt.SunToolkit; 40 import sun.util.logging.PlatformLogger; 41 42 /** 43 * A PaintManager implementation that uses a BufferStrategy for 44 * rendering. 45 * 46 * @author Scott Violet 47 */ 48 class BufferStrategyPaintManager extends RepaintManager.PaintManager { 49 // 50 // All drawing is done to a BufferStrategy. At the end of painting 51 // (endPaint) the region that was painted is flushed to the screen 52 // (using BufferStrategy.show). 53 // 54 // PaintManager.show is overriden to show directly from the 55 // BufferStrategy (when using blit), if successful true is 56 // returned and a paint event will not be generated. To avoid 57 // showing from the buffer while painting a locking scheme is 58 // implemented. When beginPaint is invoked the field painting is 59 // set to true. If painting is true and show is invoked we 60 // immediately return false. This is done to avoid blocking the 61 // toolkit thread while painting happens. In a similar way when 62 // show is invoked the field showing is set to true, beginPaint 63 // will then block until showing is true. This scheme ensures we 64 // only ever have one thread using the BufferStrategy and it also 65 // ensures the toolkit thread remains as responsive as possible. 66 // 67 // If we're using a flip strategy the contents of the backbuffer may 68 // have changed and so show only attempts to show from the backbuffer 69 // if we get a blit strategy. 70 // 71 72 // 73 // Methods used to create BufferStrategy for Applets. 74 // 75 private static Method COMPONENT_CREATE_BUFFER_STRATEGY_METHOD; 76 private static Method COMPONENT_GET_BUFFER_STRATEGY_METHOD; 77 78 private static final PlatformLogger LOGGER = PlatformLogger.getLogger( 79 "javax.swing.BufferStrategyPaintManager"); 80 81 /** 82 * List of BufferInfos. We don't use a Map primarily because 83 * there are typically only a handful of top level components making 84 * a Map overkill. 85 */ 86 private ArrayList<BufferInfo> bufferInfos; 87 88 /** 89 * Indicates <code>beginPaint</code> has been invoked. This is 90 * set to true for the life of beginPaint/endPaint pair. 91 */ 92 private boolean painting; 93 /** 94 * Indicates we're in the process of showing. All painting, on the EDT, 95 * is blocked while this is true. 96 */ 97 private boolean showing; 98 99 // 100 // Region that we need to flush. When beginPaint is called these are 101 // reset and any subsequent calls to paint/copyArea then update these 102 // fields accordingly. When endPaint is called we then try and show 103 // the accumulated region. 104 // These fields are in the coordinate system of the root. 105 // 106 private int accumulatedX; 107 private int accumulatedY; 108 private int accumulatedMaxX; 109 private int accumulatedMaxY; 110 111 // 112 // The following fields are set by prepare 113 // 114 115 /** 116 * Farthest JComponent ancestor for the current paint/copyArea. 117 */ 118 private JComponent rootJ; 119 /** 120 * Location of component being painted relative to root. 121 */ 122 private int xOffset; 123 /** 124 * Location of component being painted relative to root. 125 */ 126 private int yOffset; 127 /** 128 * Graphics from the BufferStrategy. 129 */ 130 private Graphics bsg; 131 /** 132 * BufferStrategy currently being used. 133 */ 134 private BufferStrategy bufferStrategy; 135 /** 136 * BufferInfo corresponding to root. 137 */ 138 private BufferInfo bufferInfo; 139 140 /** 141 * Set to true if the bufferInfo needs to be disposed when current 142 * paint loop is done. 143 */ 144 private boolean disposeBufferOnEnd; 145 146 private static Method getGetBufferStrategyMethod() { 147 if (COMPONENT_GET_BUFFER_STRATEGY_METHOD == null) { 148 getMethods(); 149 } 150 return COMPONENT_GET_BUFFER_STRATEGY_METHOD; 151 } 152 153 private static Method getCreateBufferStrategyMethod() { 154 if (COMPONENT_CREATE_BUFFER_STRATEGY_METHOD == null) { 155 getMethods(); 156 } 157 return COMPONENT_CREATE_BUFFER_STRATEGY_METHOD; 158 } 159 160 private static void getMethods() { 161 java.security.AccessController.doPrivileged( 162 new java.security.PrivilegedAction<Object>() { 163 public Object run() { 164 try { 165 COMPONENT_CREATE_BUFFER_STRATEGY_METHOD = Component.class. 166 getDeclaredMethod("createBufferStrategy", 167 new Class[] { int.class, 168 BufferCapabilities.class }); 169 COMPONENT_CREATE_BUFFER_STRATEGY_METHOD. 170 setAccessible(true); 171 COMPONENT_GET_BUFFER_STRATEGY_METHOD = Component.class. 172 getDeclaredMethod("getBufferStrategy"); 173 COMPONENT_GET_BUFFER_STRATEGY_METHOD.setAccessible(true); 174 } catch (SecurityException e) { 175 assert false; 176 } catch (NoSuchMethodException nsme) { 177 assert false; 178 } 179 return null; 180 } 181 }); 182 } 183 184 BufferStrategyPaintManager() { 185 bufferInfos = new ArrayList<BufferInfo>(1); 186 } 187 188 // 189 // PaintManager methods 190 // 191 192 /** 193 * Cleans up any created BufferStrategies. 194 */ 195 protected void dispose() { 196 // dipose can be invoked at any random time. To avoid 197 // threading dependancies we do the actual diposing via an 198 // invokeLater. 199 SwingUtilities.invokeLater(new Runnable() { 200 public void run() { 201 java.util.List<BufferInfo> bufferInfos; 202 synchronized(BufferStrategyPaintManager.this) { 203 while (showing) { 204 try { 205 BufferStrategyPaintManager.this.wait(); 206 } catch (InterruptedException ie) { 207 } 208 } 209 bufferInfos = BufferStrategyPaintManager.this.bufferInfos; 210 BufferStrategyPaintManager.this.bufferInfos = null; 211 } 212 dispose(bufferInfos); 213 } 214 }); 215 } 216 217 private void dispose(java.util.List<BufferInfo> bufferInfos) { 218 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 219 LOGGER.finer("BufferStrategyPaintManager disposed", 220 new RuntimeException()); 221 } 222 if (bufferInfos != null) { 223 for (BufferInfo bufferInfo : bufferInfos) { 224 bufferInfo.dispose(); 225 } 226 } 227 } 228 229 /** 230 * Shows the specified region of the back buffer. This will return 231 * true if successful, false otherwise. This is invoked on the 232 * toolkit thread in response to an expose event. 233 */ 234 public boolean show(Container c, int x, int y, int w, int h) { 235 synchronized(this) { 236 if (painting) { 237 // Don't show from backbuffer while in the process of 238 // painting. 239 return false; 240 } 241 showing = true; 242 } 243 try { 244 BufferInfo info = getBufferInfo(c); 245 BufferStrategy bufferStrategy; 246 if (info != null && info.isInSync() && 247 (bufferStrategy = info.getBufferStrategy(false)) != null) { 248 SubRegionShowable bsSubRegion = 249 (SubRegionShowable)bufferStrategy; 250 boolean paintAllOnExpose = info.getPaintAllOnExpose(); 251 info.setPaintAllOnExpose(false); 252 if (bsSubRegion.showIfNotLost(x, y, (x + w), (y + h))) { 253 return !paintAllOnExpose; 254 } 255 // Mark the buffer as needing to be repainted. We don't 256 // immediately do a repaint as this method will return false 257 // indicating a PaintEvent should be generated which will 258 // trigger a complete repaint. 259 bufferInfo.setContentsLostDuringExpose(true); 260 } 261 } 262 finally { 263 synchronized(this) { 264 showing = false; 265 notifyAll(); 266 } 267 } 268 return false; 269 } 270 271 public boolean paint(JComponent paintingComponent, 272 JComponent bufferComponent, Graphics g, 273 int x, int y, int w, int h) { 274 Container root = fetchRoot(paintingComponent); 275 276 if (prepare(paintingComponent, root, true, x, y, w, h)) { 277 if ((g instanceof SunGraphics2D) && 278 ((SunGraphics2D)g).getDestination() == root) { 279 // BufferStrategy may have already constrained the Graphics. To 280 // account for that we revert the constrain, then apply a 281 // constrain for Swing on top of that. 282 int cx = ((SunGraphics2D)bsg).constrainX; 283 int cy = ((SunGraphics2D)bsg).constrainY; 284 if (cx != 0 || cy != 0) { 285 bsg.translate(-cx, -cy); 286 } 287 ((SunGraphics2D)bsg).constrain(xOffset + cx, yOffset + cy, 288 x + w, y + h); 289 bsg.setClip(x, y, w, h); 290 paintingComponent.paintToOffscreen(bsg, x, y, w, h, 291 x + w, y + h); 292 accumulate(xOffset + x, yOffset + y, w, h); 293 return true; 294 } else { 295 // Assume they are going to eventually render to the screen. 296 // This disables showing from backbuffer until a complete 297 // repaint occurs. 298 bufferInfo.setInSync(false); 299 // Fall through to old rendering. 300 } 301 } 302 // Invalid root, do what Swing has always done. 303 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 304 LOGGER.finer("prepare failed"); 305 } 306 return super.paint(paintingComponent, bufferComponent, g, x, y, w, h); 307 } 308 309 public void copyArea(JComponent c, Graphics g, int x, int y, int w, int h, 310 int deltaX, int deltaY, boolean clip) { 311 // Note: this method is only called internally and we know that 312 // g is from a heavyweight Component, so no check is necessary as 313 // it is in paint() above. 314 // 315 // If the buffer isn't in sync there is no point in doing a copyArea, 316 // it has garbage. 317 Container root = fetchRoot(c); 318 319 if (prepare(c, root, false, 0, 0, 0, 0) && bufferInfo.isInSync()) { 320 if (clip) { 321 Rectangle cBounds = c.getVisibleRect(); 322 int relX = xOffset + x; 323 int relY = yOffset + y; 324 bsg.clipRect(xOffset + cBounds.x, 325 yOffset + cBounds.y, 326 cBounds.width, cBounds.height); 327 bsg.copyArea(relX, relY, w, h, deltaX, deltaY); 328 } 329 else { 330 bsg.copyArea(xOffset + x, yOffset + y, w, h, deltaX, 331 deltaY); 332 } 333 accumulate(x + xOffset + deltaX, y + yOffset + deltaY, w, h); 334 } else { 335 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 336 LOGGER.finer("copyArea: prepare failed or not in sync"); 337 } 338 // Prepare failed, or not in sync. By calling super.copyArea 339 // we'll copy on screen. We need to flush any pending paint to 340 // the screen otherwise we'll do a copyArea on the wrong thing. 341 if (!flushAccumulatedRegion()) { 342 // Flush failed, copyArea will be copying garbage, 343 // force repaint of all. 344 rootJ.repaint(); 345 } else { 346 super.copyArea(c, g, x, y, w, h, deltaX, deltaY, clip); 347 } 348 } 349 } 350 351 public void beginPaint() { 352 synchronized(this) { 353 painting = true; 354 // Make sure another thread isn't attempting to show from 355 // the back buffer. 356 while(showing) { 357 try { 358 wait(); 359 } catch (InterruptedException ie) { 360 } 361 } 362 } 363 if (LOGGER.isLoggable(PlatformLogger.FINEST)) { 364 LOGGER.finest("beginPaint"); 365 } 366 // Reset the area that needs to be painted. 367 resetAccumulated(); 368 } 369 370 public void endPaint() { 371 if (LOGGER.isLoggable(PlatformLogger.FINEST)) { 372 LOGGER.finest("endPaint: region " + accumulatedX + " " + 373 accumulatedY + " " + accumulatedMaxX + " " + 374 accumulatedMaxY); 375 } 376 if (painting) { 377 if (!flushAccumulatedRegion()) { 378 if (!isRepaintingRoot()) { 379 repaintRoot(rootJ); 380 } 381 else { 382 // Contents lost twice in a row, punt. 383 resetDoubleBufferPerWindow(); 384 // In case we've left junk on the screen, force a repaint. 385 rootJ.repaint(); 386 } 387 } 388 } 389 390 BufferInfo toDispose = null; 391 synchronized(this) { 392 painting = false; 393 if (disposeBufferOnEnd) { 394 disposeBufferOnEnd = false; 395 toDispose = bufferInfo; 396 bufferInfos.remove(toDispose); 397 } 398 } 399 if (toDispose != null) { 400 toDispose.dispose(); 401 } 402 } 403 404 /** 405 * Renders the BufferStrategy to the screen. 406 * 407 * @return true if successful, false otherwise. 408 */ 409 private boolean flushAccumulatedRegion() { 410 boolean success = true; 411 if (accumulatedX != Integer.MAX_VALUE) { 412 SubRegionShowable bsSubRegion = (SubRegionShowable)bufferStrategy; 413 boolean contentsLost = bufferStrategy.contentsLost(); 414 if (!contentsLost) { 415 bsSubRegion.show(accumulatedX, accumulatedY, 416 accumulatedMaxX, accumulatedMaxY); 417 contentsLost = bufferStrategy.contentsLost(); 418 } 419 if (contentsLost) { 420 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 421 LOGGER.finer("endPaint: contents lost"); 422 } 423 // Shown region was bogus, mark buffer as out of sync. 424 bufferInfo.setInSync(false); 425 success = false; 426 } 427 } 428 resetAccumulated(); 429 return success; 430 } 431 432 private void resetAccumulated() { 433 accumulatedX = Integer.MAX_VALUE; 434 accumulatedY = Integer.MAX_VALUE; 435 accumulatedMaxX = 0; 436 accumulatedMaxY = 0; 437 } 438 439 /** 440 * Invoked when the double buffering or useTrueDoubleBuffering 441 * changes for a JRootPane. If the rootpane is not double 442 * buffered, or true double buffering changes we throw out any 443 * cache we may have. 444 */ 445 public void doubleBufferingChanged(final JRootPane rootPane) { 446 if ((!rootPane.isDoubleBuffered() || 447 !rootPane.getUseTrueDoubleBuffering()) && 448 rootPane.getParent() != null) { 449 if (!SwingUtilities.isEventDispatchThread()) { 450 Runnable updater = new Runnable() { 451 public void run() { 452 doubleBufferingChanged0(rootPane); 453 } 454 }; 455 SwingUtilities.invokeLater(updater); 456 } 457 else { 458 doubleBufferingChanged0(rootPane); 459 } 460 } 461 } 462 463 /** 464 * Does the work for doubleBufferingChanged. 465 */ 466 private void doubleBufferingChanged0(JRootPane rootPane) { 467 // This will only happen on the EDT. 468 BufferInfo info; 469 synchronized(this) { 470 // Make sure another thread isn't attempting to show from 471 // the back buffer. 472 while(showing) { 473 try { 474 wait(); 475 } catch (InterruptedException ie) { 476 } 477 } 478 info = getBufferInfo(rootPane.getParent()); 479 if (painting && bufferInfo == info) { 480 // We're in the process of painting and the user grabbed 481 // the Graphics. If we dispose now, endPaint will attempt 482 // to show a bogus BufferStrategy. Set a flag so that 483 // endPaint knows it needs to dispose this buffer. 484 disposeBufferOnEnd = true; 485 info = null; 486 } else if (info != null) { 487 bufferInfos.remove(info); 488 } 489 } 490 if (info != null) { 491 info.dispose(); 492 } 493 } 494 495 /** 496 * Calculates information common to paint/copyArea. 497 * 498 * @return true if should use buffering per window in painting. 499 */ 500 private boolean prepare(JComponent c, Container root, boolean isPaint, int x, int y, 501 int w, int h) { 502 if (bsg != null) { 503 bsg.dispose(); 504 bsg = null; 505 } 506 bufferStrategy = null; 507 if (root != null) { 508 boolean contentsLost = false; 509 BufferInfo bufferInfo = getBufferInfo(root); 510 if (bufferInfo == null) { 511 contentsLost = true; 512 bufferInfo = new BufferInfo(root); 513 bufferInfos.add(bufferInfo); 514 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 515 LOGGER.finer("prepare: new BufferInfo: " + root); 516 } 517 } 518 this.bufferInfo = bufferInfo; 519 if (!bufferInfo.hasBufferStrategyChanged()) { 520 bufferStrategy = bufferInfo.getBufferStrategy(true); 521 if (bufferStrategy != null) { 522 bsg = bufferStrategy.getDrawGraphics(); 523 if (bufferStrategy.contentsRestored()) { 524 contentsLost = true; 525 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 526 LOGGER.finer("prepare: contents restored in prepare"); 527 } 528 } 529 } 530 else { 531 // Couldn't create BufferStrategy, fallback to normal 532 // painting. 533 return false; 534 } 535 if (bufferInfo.getContentsLostDuringExpose()) { 536 contentsLost = true; 537 bufferInfo.setContentsLostDuringExpose(false); 538 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 539 LOGGER.finer("prepare: contents lost on expose"); 540 } 541 } 542 if (isPaint && c == rootJ && x == 0 && y == 0 && 543 c.getWidth() == w && c.getHeight() == h) { 544 bufferInfo.setInSync(true); 545 } 546 else if (contentsLost) { 547 // We either recreated the BufferStrategy, or the contents 548 // of the buffer strategy were restored. We need to 549 // repaint the root pane so that the back buffer is in sync 550 // again. 551 bufferInfo.setInSync(false); 552 if (!isRepaintingRoot()) { 553 repaintRoot(rootJ); 554 } 555 else { 556 // Contents lost twice in a row, punt 557 resetDoubleBufferPerWindow(); 558 } 559 } 560 return (bufferInfos != null); 561 } 562 } 563 return false; 564 } 565 566 private Container fetchRoot(JComponent c) { 567 boolean encounteredHW = false; 568 rootJ = c; 569 Container root = c; 570 xOffset = yOffset = 0; 571 while (root != null && 572 (!(root instanceof Window) && 573 !SunToolkit.isInstanceOf(root, "java.applet.Applet"))) { 574 xOffset += root.getX(); 575 yOffset += root.getY(); 576 root = root.getParent(); 577 if (root != null) { 578 if (root instanceof JComponent) { 579 rootJ = (JComponent)root; 580 } 581 else if (!root.isLightweight()) { 582 if (!encounteredHW) { 583 encounteredHW = true; 584 } 585 else { 586 // We've encountered two hws now and may have 587 // a containment hierarchy with lightweights containing 588 // heavyweights containing other lightweights. 589 // Heavyweights poke holes in lightweight 590 // rendering so that if we call show on the BS 591 // (which is associated with the Window) you will 592 // not see the contents over any child 593 // heavyweights. If we didn't do this when we 594 // went to show the descendants of the nested hw 595 // you would see nothing, so, we bail out here. 596 return null; 597 } 598 } 599 } 600 } 601 if ((root instanceof RootPaneContainer) && 602 (rootJ instanceof JRootPane)) { 603 // We're in a Swing heavyeight (JFrame/JWindow...), use double 604 // buffering if double buffering enabled on the JRootPane and 605 // the JRootPane wants true double buffering. 606 if (rootJ.isDoubleBuffered() && 607 ((JRootPane)rootJ).getUseTrueDoubleBuffering()) { 608 // Whether or not a component is double buffered is a 609 // bit tricky with Swing. This gives a good approximation 610 // of the various ways to turn on double buffering for 611 // components. 612 return root; 613 } 614 } 615 // Don't do true double buffering. 616 return null; 617 } 618 619 /** 620 * Turns off double buffering per window. 621 */ 622 private void resetDoubleBufferPerWindow() { 623 if (bufferInfos != null) { 624 dispose(bufferInfos); 625 bufferInfos = null; 626 repaintManager.setPaintManager(null); 627 } 628 } 629 630 /** 631 * Returns the BufferInfo for the specified root or null if one 632 * hasn't been created yet. 633 */ 634 private BufferInfo getBufferInfo(Container root) { 635 for (int counter = bufferInfos.size() - 1; counter >= 0; counter--) { 636 BufferInfo bufferInfo = bufferInfos.get(counter); 637 Container biRoot = bufferInfo.getRoot(); 638 if (biRoot == null) { 639 // Window gc'ed 640 bufferInfos.remove(counter); 641 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 642 LOGGER.finer("BufferInfo pruned, root null"); 643 } 644 } 645 else if (biRoot == root) { 646 return bufferInfo; 647 } 648 } 649 return null; 650 } 651 652 private void accumulate(int x, int y, int w, int h) { 653 accumulatedX = Math.min(x, accumulatedX); 654 accumulatedY = Math.min(y, accumulatedY); 655 accumulatedMaxX = Math.max(accumulatedMaxX, x + w); 656 accumulatedMaxY = Math.max(accumulatedMaxY, y + h); 657 } 658 659 660 661 /** 662 * BufferInfo is used to track the BufferStrategy being used for 663 * a particular Component. In addition to tracking the BufferStrategy 664 * it will install a WindowListener and ComponentListener. When the 665 * component is hidden/iconified the buffer is marked as needing to be 666 * completely repainted. 667 */ 668 private class BufferInfo extends ComponentAdapter implements 669 WindowListener { 670 // NOTE: This class does NOT hold a direct reference to the root, if it 671 // did there would be a cycle between the BufferPerWindowPaintManager 672 // and the Window so that it could never be GC'ed 673 // 674 // Reference to BufferStrategy is referenced via WeakReference for 675 // same reason. 676 private WeakReference<BufferStrategy> weakBS; 677 private WeakReference<Container> root; 678 // Indicates whether or not the backbuffer and display are in sync. 679 // This is set to true when a full repaint on the rootpane is done. 680 private boolean inSync; 681 // Indicates the contents were lost during and expose event. 682 private boolean contentsLostDuringExpose; 683 // Indicates we need to generate a paint event on expose. 684 private boolean paintAllOnExpose; 685 686 687 public BufferInfo(Container root) { 688 this.root = new WeakReference<Container>(root); 689 root.addComponentListener(this); 690 if (root instanceof Window) { 691 ((Window)root).addWindowListener(this); 692 } 693 } 694 695 public void setPaintAllOnExpose(boolean paintAllOnExpose) { 696 this.paintAllOnExpose = paintAllOnExpose; 697 } 698 699 public boolean getPaintAllOnExpose() { 700 return paintAllOnExpose; 701 } 702 703 public void setContentsLostDuringExpose(boolean value) { 704 contentsLostDuringExpose = value; 705 } 706 707 public boolean getContentsLostDuringExpose() { 708 return contentsLostDuringExpose; 709 } 710 711 public void setInSync(boolean inSync) { 712 this.inSync = inSync; 713 } 714 715 /** 716 * Whether or not the contents of the buffer strategy 717 * is in sync with the window. This is set to true when the root 718 * pane paints all, and false when contents are lost/restored. 719 */ 720 public boolean isInSync() { 721 return inSync; 722 } 723 724 /** 725 * Returns the Root (Window or Applet) that this BufferInfo references. 726 */ 727 public Container getRoot() { 728 return (root == null) ? null : root.get(); 729 } 730 731 /** 732 * Returns the BufferStartegy. This will return null if 733 * the BufferStartegy hasn't been created and <code>create</code> is 734 * false, or if there is a problem in creating the 735 * <code>BufferStartegy</code>. 736 * 737 * @param create If true, and the BufferStartegy is currently null, 738 * one will be created. 739 */ 740 public BufferStrategy getBufferStrategy(boolean create) { 741 BufferStrategy bs = (weakBS == null) ? null : weakBS.get(); 742 if (bs == null && create) { 743 bs = createBufferStrategy(); 744 if (bs != null) { 745 weakBS = new WeakReference<BufferStrategy>(bs); 746 } 747 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 748 LOGGER.finer("getBufferStrategy: created bs: " + bs); 749 } 750 } 751 return bs; 752 } 753 754 /** 755 * Returns true if the buffer strategy of the component differs 756 * from current buffer strategy. 757 */ 758 public boolean hasBufferStrategyChanged() { 759 Container root = getRoot(); 760 if (root != null) { 761 BufferStrategy ourBS = null; 762 BufferStrategy componentBS = null; 763 764 ourBS = getBufferStrategy(false); 765 if (root instanceof Window) { 766 componentBS = ((Window)root).getBufferStrategy(); 767 } 768 else { 769 try { 770 componentBS = (BufferStrategy) 771 getGetBufferStrategyMethod().invoke(root); 772 } catch (InvocationTargetException ite) { 773 assert false; 774 } catch (IllegalArgumentException iae) { 775 assert false; 776 } catch (IllegalAccessException iae2) { 777 assert false; 778 } 779 } 780 if (componentBS != ourBS) { 781 // Component has a different BS, dispose ours. 782 if (ourBS != null) { 783 ourBS.dispose(); 784 } 785 weakBS = null; 786 return true; 787 } 788 } 789 return false; 790 } 791 792 /** 793 * Creates the BufferStrategy. If the appropriate system property 794 * has been set we'll try for flip first and then we'll try for 795 * blit. 796 */ 797 private BufferStrategy createBufferStrategy() { 798 Container root = getRoot(); 799 if (root == null) { 800 return null; 801 } 802 BufferStrategy bs = null; 803 if (SwingUtilities3.isVsyncRequested(root)) { 804 bs = createBufferStrategy(root, true); 805 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 806 LOGGER.finer("createBufferStrategy: using vsynced strategy"); 807 } 808 } 809 if (bs == null) { 810 bs = createBufferStrategy(root, false); 811 } 812 if (!(bs instanceof SubRegionShowable)) { 813 // We do this for two reasons: 814 // 1. So that we know we can cast to SubRegionShowable and 815 // invoke show with the minimal region to update 816 // 2. To avoid the possibility of invoking client code 817 // on the toolkit thread. 818 bs = null; 819 } 820 return bs; 821 } 822 823 // Creates and returns a buffer strategy. If 824 // there is a problem creating the buffer strategy this will 825 // eat the exception and return null. 826 private BufferStrategy createBufferStrategy(Container root, 827 boolean isVsynced) { 828 BufferCapabilities caps; 829 if (isVsynced) { 830 caps = new ExtendedBufferCapabilities( 831 new ImageCapabilities(true), new ImageCapabilities(true), 832 BufferCapabilities.FlipContents.COPIED, 833 ExtendedBufferCapabilities.VSyncType.VSYNC_ON); 834 } else { 835 caps = new BufferCapabilities( 836 new ImageCapabilities(true), new ImageCapabilities(true), 837 null); 838 } 839 BufferStrategy bs = null; 840 if (SunToolkit.isInstanceOf(root, "java.applet.Applet")) { 841 try { 842 getCreateBufferStrategyMethod().invoke(root, 2, caps); 843 bs = (BufferStrategy)getGetBufferStrategyMethod(). 844 invoke(root); 845 } catch (InvocationTargetException ite) { 846 // Type is not supported 847 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 848 LOGGER.finer("createBufferStratety failed", 849 ite); 850 } 851 } catch (IllegalArgumentException iae) { 852 assert false; 853 } catch (IllegalAccessException iae2) { 854 assert false; 855 } 856 } 857 else { 858 try { 859 ((Window)root).createBufferStrategy(2, caps); 860 bs = ((Window)root).getBufferStrategy(); 861 } catch (AWTException e) { 862 // Type not supported 863 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 864 LOGGER.finer("createBufferStratety failed", 865 e); 866 } 867 } 868 } 869 return bs; 870 } 871 872 /** 873 * Cleans up and removes any references. 874 */ 875 public void dispose() { 876 Container root = getRoot(); 877 if (LOGGER.isLoggable(PlatformLogger.FINER)) { 878 LOGGER.finer("disposed BufferInfo for: " + root); 879 } 880 if (root != null) { 881 root.removeComponentListener(this); 882 if (root instanceof Window) { 883 ((Window)root).removeWindowListener(this); 884 } 885 BufferStrategy bs = getBufferStrategy(false); 886 if (bs != null) { 887 bs.dispose(); 888 } 889 } 890 this.root = null; 891 weakBS = null; 892 } 893 894 // We mark the buffer as needing to be painted on a hide/iconify 895 // because the developer may have conditionalized painting based on 896 // visibility. 897 // Ideally we would also move to having the BufferStrategy being 898 // a SoftReference in Component here, but that requires changes to 899 // Component and the like. 900 public void componentHidden(ComponentEvent e) { 901 Container root = getRoot(); 902 if (root != null && root.isVisible()) { 903 // This case will only happen if a developer calls 904 // hide immediately followed by show. In this case 905 // the event is delivered after show and the window 906 // will still be visible. If a developer altered the 907 // contents of the window between the hide/show 908 // invocations we won't recognize we need to paint and 909 // the contents would be bogus. Calling repaint here 910 // fixs everything up. 911 root.repaint(); 912 } 913 else { 914 setPaintAllOnExpose(true); 915 } 916 } 917 918 public void windowIconified(WindowEvent e) { 919 setPaintAllOnExpose(true); 920 } 921 922 // On a dispose we chuck everything. 923 public void windowClosed(WindowEvent e) { 924 // Make sure we're not showing. 925 synchronized(BufferStrategyPaintManager.this) { 926 while (showing) { 927 try { 928 BufferStrategyPaintManager.this.wait(); 929 } catch (InterruptedException ie) { 930 } 931 } 932 bufferInfos.remove(this); 933 } 934 dispose(); 935 } 936 937 public void windowOpened(WindowEvent e) { 938 } 939 940 public void windowClosing(WindowEvent e) { 941 } 942 943 public void windowDeiconified(WindowEvent e) { 944 } 945 946 public void windowActivated(WindowEvent e) { 947 } 948 949 public void windowDeactivated(WindowEvent e) { 950 } 951 } 952 }