1 /* 2 * Copyright (c) 2005, 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 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 getGetBufferStrategyMethod()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 getCreateBufferStrategyMethod()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 getMethods()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 BufferStrategyPaintManager()184 BufferStrategyPaintManager() { 185 bufferInfos = new ArrayList<BufferInfo>(1); 186 } 187 188 // 189 // PaintManager methods 190 // 191 192 /** 193 * Cleans up any created BufferStrategies. 194 */ dispose()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 dispose(java.util.List<BufferInfo> bufferInfos)217 private void dispose(java.util.List<BufferInfo> bufferInfos) { 218 if (LOGGER.isLoggable(PlatformLogger.Level.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 */ show(Container c, int x, int y, int w, int h)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 paint(JComponent paintingComponent, JComponent bufferComponent, Graphics g, int x, int y, int w, int h)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.Level.FINER)) { 304 LOGGER.finer("prepare failed"); 305 } 306 return super.paint(paintingComponent, bufferComponent, g, x, y, w, h); 307 } 308 copyArea(JComponent c, Graphics g, int x, int y, int w, int h, int deltaX, int deltaY, boolean clip)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.Level.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 beginPaint()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.Level.FINEST)) { 364 LOGGER.finest("beginPaint"); 365 } 366 // Reset the area that needs to be painted. 367 resetAccumulated(); 368 } 369 endPaint()370 public void endPaint() { 371 if (LOGGER.isLoggable(PlatformLogger.Level.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 */ flushAccumulatedRegion()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.Level.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 resetAccumulated()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 */ doubleBufferingChanged(final JRootPane rootPane)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 */ doubleBufferingChanged0(JRootPane rootPane)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 */ prepare(JComponent c, Container root, boolean isPaint, int x, int y, int w, int h)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.Level.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.Level.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.Level.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 fetchRoot(JComponent c)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 */ resetDoubleBufferPerWindow()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 */ getBufferInfo(Container root)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.Level.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 accumulate(int x, int y, int w, int h)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 BufferInfo(Container root)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 setPaintAllOnExpose(boolean paintAllOnExpose)695 public void setPaintAllOnExpose(boolean paintAllOnExpose) { 696 this.paintAllOnExpose = paintAllOnExpose; 697 } 698 getPaintAllOnExpose()699 public boolean getPaintAllOnExpose() { 700 return paintAllOnExpose; 701 } 702 setContentsLostDuringExpose(boolean value)703 public void setContentsLostDuringExpose(boolean value) { 704 contentsLostDuringExpose = value; 705 } 706 getContentsLostDuringExpose()707 public boolean getContentsLostDuringExpose() { 708 return contentsLostDuringExpose; 709 } 710 setInSync(boolean inSync)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 */ isInSync()720 public boolean isInSync() { 721 return inSync; 722 } 723 724 /** 725 * Returns the Root (Window or Applet) that this BufferInfo references. 726 */ getRoot()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 */ getBufferStrategy(boolean create)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.Level.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 */ hasBufferStrategyChanged()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 */ createBufferStrategy()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.Level.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. createBufferStrategy(Container root, boolean isVsynced)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.Level.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.Level.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 */ dispose()875 public void dispose() { 876 Container root = getRoot(); 877 if (LOGGER.isLoggable(PlatformLogger.Level.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. componentHidden(ComponentEvent e)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 windowIconified(WindowEvent e)918 public void windowIconified(WindowEvent e) { 919 setPaintAllOnExpose(true); 920 } 921 922 // On a dispose we chuck everything. windowClosed(WindowEvent e)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 windowOpened(WindowEvent e)937 public void windowOpened(WindowEvent e) { 938 } 939 windowClosing(WindowEvent e)940 public void windowClosing(WindowEvent e) { 941 } 942 windowDeiconified(WindowEvent e)943 public void windowDeiconified(WindowEvent e) { 944 } 945 windowActivated(WindowEvent e)946 public void windowActivated(WindowEvent e) { 947 } 948 windowDeactivated(WindowEvent e)949 public void windowDeactivated(WindowEvent e) { 950 } 951 } 952 } 953