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