1 /* 2 * Copyright (c) 1997, 2015, 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 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.awt.image.VolatileImage; 31 import java.security.AccessControlContext; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.util.*; 35 import java.util.concurrent.atomic.AtomicInteger; 36 import java.applet.*; 37 38 import jdk.internal.misc.JavaSecurityAccess; 39 import jdk.internal.misc.SharedSecrets; 40 import sun.awt.AWTAccessor; 41 import sun.awt.AppContext; 42 import sun.awt.DisplayChangedListener; 43 import sun.awt.SunToolkit; 44 import sun.java2d.SunGraphicsEnvironment; 45 import sun.security.action.GetPropertyAction; 46 47 import com.sun.java.swing.SwingUtilities3; 48 import java.awt.geom.AffineTransform; 49 import sun.java2d.SunGraphics2D; 50 import sun.java2d.pipe.Region; 51 import sun.swing.SwingAccessor; 52 import sun.swing.SwingUtilities2; 53 import sun.swing.SwingUtilities2.RepaintListener; 54 55 /** 56 * This class manages repaint requests, allowing the number 57 * of repaints to be minimized, for example by collapsing multiple 58 * requests into a single repaint for members of a component tree. 59 * <p> 60 * As of 1.6 <code>RepaintManager</code> handles repaint requests 61 * for Swing's top level components (<code>JApplet</code>, 62 * <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>). 63 * Any calls to <code>repaint</code> on one of these will call into the 64 * appropriate <code>addDirtyRegion</code> method. 65 * 66 * @author Arnaud Weber 67 * @since 1.2 68 */ 69 public class RepaintManager 70 { 71 /** 72 * Whether or not the RepaintManager should handle paint requests 73 * for top levels. 74 */ 75 static final boolean HANDLE_TOP_LEVEL_PAINT; 76 77 private static final short BUFFER_STRATEGY_NOT_SPECIFIED = 0; 78 private static final short BUFFER_STRATEGY_SPECIFIED_ON = 1; 79 private static final short BUFFER_STRATEGY_SPECIFIED_OFF = 2; 80 81 private static final short BUFFER_STRATEGY_TYPE; 82 83 /** 84 * Maps from GraphicsConfiguration to VolatileImage. 85 */ 86 private Map<GraphicsConfiguration,VolatileImage> volatileMap = new 87 HashMap<GraphicsConfiguration,VolatileImage>(1); 88 89 // 90 // As of 1.6 Swing handles scheduling of paint events from native code. 91 // That is, SwingPaintEventDispatcher is invoked on the toolkit thread, 92 // which in turn invokes nativeAddDirtyRegion. Because this is invoked 93 // from the native thread we can not invoke any public methods and so 94 // we introduce these added maps. So, any time nativeAddDirtyRegion is 95 // invoked the region is added to hwDirtyComponents and a work request 96 // is scheduled. When the work request is processed all entries in 97 // this map are pushed to the real map (dirtyComponents) and then 98 // painted with the rest of the components. 99 // 100 private Map<Container,Rectangle> hwDirtyComponents; 101 102 private Map<Component,Rectangle> dirtyComponents; 103 private Map<Component,Rectangle> tmpDirtyComponents; 104 private java.util.List<Component> invalidComponents; 105 106 // List of Runnables that need to be processed before painting from AWT. 107 private java.util.List<Runnable> runnableList; 108 109 boolean doubleBufferingEnabled = true; 110 111 private Dimension doubleBufferMaxSize; 112 113 // Support for both the standard and volatile offscreen buffers exists to 114 // provide backwards compatibility for the [rare] programs which may be 115 // calling getOffScreenBuffer() and not expecting to get a VolatileImage. 116 // Swing internally is migrating to use *only* the volatile image buffer. 117 118 // Support for standard offscreen buffer 119 // 120 DoubleBufferInfo standardDoubleBuffer; 121 122 /** 123 * Object responsible for hanlding core paint functionality. 124 */ 125 private PaintManager paintManager; 126 127 private static final Object repaintManagerKey = RepaintManager.class; 128 129 // Whether or not a VolatileImage should be used for double-buffered painting 130 static boolean volatileImageBufferEnabled = true; 131 /** 132 * Type of VolatileImage which should be used for double-buffered 133 * painting. 134 */ 135 private static final int volatileBufferType; 136 /** 137 * Value of the system property awt.nativeDoubleBuffering. 138 */ 139 private static boolean nativeDoubleBuffering; 140 141 // The maximum number of times Swing will attempt to use the VolatileImage 142 // buffer during a paint operation. 143 private static final int VOLATILE_LOOP_MAX = 2; 144 145 /** 146 * Number of <code>beginPaint</code> that have been invoked. 147 */ 148 private int paintDepth = 0; 149 150 /** 151 * Type of buffer strategy to use. Will be one of the BUFFER_STRATEGY_ 152 * constants. 153 */ 154 private short bufferStrategyType; 155 156 // 157 // BufferStrategyPaintManager has the unique characteristic that it 158 // must deal with the buffer being lost while painting to it. For 159 // example, if we paint a component and show it and the buffer has 160 // become lost we must repaint the whole window. To deal with that 161 // the PaintManager calls into repaintRoot, and if we're still in 162 // the process of painting the repaintRoot field is set to the JRootPane 163 // and after the current JComponent.paintImmediately call finishes 164 // paintImmediately will be invoked on the repaintRoot. In this 165 // way we don't try to show garbage to the screen. 166 // 167 /** 168 * True if we're in the process of painting the dirty regions. This is 169 * set to true in <code>paintDirtyRegions</code>. 170 */ 171 private boolean painting; 172 /** 173 * If the PaintManager calls into repaintRoot during painting this field 174 * will be set to the root. 175 */ 176 private JComponent repaintRoot; 177 178 /** 179 * The Thread that has initiated painting. If null it 180 * indicates painting is not currently in progress. 181 */ 182 private Thread paintThread; 183 184 /** 185 * Runnable used to process all repaint/revalidate requests. 186 */ 187 private final ProcessingRunnable processingRunnable; 188 189 private static final JavaSecurityAccess javaSecurityAccess = 190 SharedSecrets.getJavaSecurityAccess(); 191 192 /** 193 * Listener installed to detect display changes. When display changes, 194 * schedules a callback to notify all RepaintManagers of the display 195 * changes. 196 */ 197 private static final DisplayChangedListener displayChangedHandler = 198 new DisplayChangedHandler(); 199 200 static { 201 SwingAccessor.setRepaintManagerAccessor(new SwingAccessor.RepaintManagerAccessor() { 202 @Override 203 public void addRepaintListener(RepaintManager rm, RepaintListener l) { 204 rm.addRepaintListener(l); 205 } 206 @Override 207 public void removeRepaintListener(RepaintManager rm, RepaintListener l) { 208 rm.removeRepaintListener(l); 209 } 210 }); 211 212 volatileImageBufferEnabled = "true".equals(AccessController. 213 doPrivileged(new GetPropertyAction( 214 "swing.volatileImageBufferEnabled", "true"))); 215 boolean headless = GraphicsEnvironment.isHeadless(); 216 if (volatileImageBufferEnabled && headless) { 217 volatileImageBufferEnabled = false; 218 } 219 nativeDoubleBuffering = "true".equals(AccessController.doPrivileged( 220 new GetPropertyAction("awt.nativeDoubleBuffering"))); 221 String bs = AccessController.doPrivileged( 222 new GetPropertyAction("swing.bufferPerWindow")); 223 if (headless) { 224 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF; 225 } 226 else if (bs == null) { 227 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED; 228 } 229 else if ("true".equals(bs)) { 230 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON; 231 } 232 else { 233 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF; 234 } 235 HANDLE_TOP_LEVEL_PAINT = "true".equals(AccessController.doPrivileged( 236 new GetPropertyAction("swing.handleTopLevelPaint", "true"))); 237 GraphicsEnvironment ge = GraphicsEnvironment. 238 getLocalGraphicsEnvironment(); 239 if (ge instanceof SunGraphicsEnvironment) { 240 ((SunGraphicsEnvironment) ge).addDisplayChangedListener( 241 displayChangedHandler); 242 } 243 Toolkit tk = Toolkit.getDefaultToolkit(); 244 if ((tk instanceof SunToolkit) 245 && ((SunToolkit) tk).isSwingBackbufferTranslucencySupported()) { 246 volatileBufferType = Transparency.TRANSLUCENT; 247 } else { 248 volatileBufferType = Transparency.OPAQUE; 249 } 250 } 251 252 /** 253 * Return the RepaintManager for the calling thread given a Component. 254 * 255 * @param c a Component -- unused in the default implementation, but could 256 * be used by an overridden version to return a different RepaintManager 257 * depending on the Component 258 * @return the RepaintManager object 259 */ 260 public static RepaintManager currentManager(Component c) { 261 // Note: DisplayChangedRunnable passes in null as the component, so if 262 // component is ever used to determine the current 263 // RepaintManager, DisplayChangedRunnable will need to be modified 264 // accordingly. 265 return currentManager(AppContext.getAppContext()); 266 } 267 268 /** 269 * Returns the RepaintManager for the specified AppContext. If 270 * a RepaintManager has not been created for the specified 271 * AppContext this will return null. 272 */ 273 static RepaintManager currentManager(AppContext appContext) { 274 RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey); 275 if (rm == null) { 276 rm = new RepaintManager(BUFFER_STRATEGY_TYPE); 277 appContext.put(repaintManagerKey, rm); 278 } 279 return rm; 280 } 281 282 /** 283 * Return the RepaintManager for the calling thread given a JComponent. 284 * <p> 285 * Note: This method exists for backward binary compatibility with earlier 286 * versions of the Swing library. It simply returns the result returned by 287 * {@link #currentManager(Component)}. 288 * 289 * @param c a JComponent -- unused 290 * @return the RepaintManager object 291 */ 292 public static RepaintManager currentManager(JComponent c) { 293 return currentManager((Component)c); 294 } 295 296 297 /** 298 * Set the RepaintManager that should be used for the calling 299 * thread. <b>aRepaintManager</b> will become the current RepaintManager 300 * for the calling thread's thread group. 301 * @param aRepaintManager the RepaintManager object to use 302 */ 303 public static void setCurrentManager(RepaintManager aRepaintManager) { 304 if (aRepaintManager != null) { 305 SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager); 306 } else { 307 SwingUtilities.appContextRemove(repaintManagerKey); 308 } 309 } 310 311 /** 312 * Create a new RepaintManager instance. You rarely call this constructor. 313 * directly. To get the default RepaintManager, use 314 * RepaintManager.currentManager(JComponent) (normally "this"). 315 */ 316 public RepaintManager() { 317 // Because we can't know what a subclass is doing with the 318 // volatile image we immediately punt in subclasses. If this 319 // poses a problem we'll need a more sophisticated detection algorithm, 320 // or API. 321 this(BUFFER_STRATEGY_SPECIFIED_OFF); 322 } 323 324 private RepaintManager(short bufferStrategyType) { 325 // If native doublebuffering is being used, do NOT use 326 // Swing doublebuffering. 327 doubleBufferingEnabled = !nativeDoubleBuffering; 328 synchronized(this) { 329 dirtyComponents = new IdentityHashMap<Component,Rectangle>(); 330 tmpDirtyComponents = new IdentityHashMap<Component,Rectangle>(); 331 this.bufferStrategyType = bufferStrategyType; 332 hwDirtyComponents = new IdentityHashMap<Container,Rectangle>(); 333 } 334 processingRunnable = new ProcessingRunnable(); 335 } 336 337 private void displayChanged() { 338 clearImages(); 339 340 // Reset buffer maximum size to get valid size from updated graphics 341 // environment in getDoubleBufferMaximumSize() 342 setDoubleBufferMaximumSize(null); 343 } 344 345 /** 346 * Mark the component as in need of layout and queue a runnable 347 * for the event dispatching thread that will validate the components 348 * first isValidateRoot() ancestor. 349 * 350 * @param invalidComponent a component 351 * @see JComponent#isValidateRoot 352 * @see #removeInvalidComponent 353 */ 354 public synchronized void addInvalidComponent(JComponent invalidComponent) 355 { 356 RepaintManager delegate = getDelegate(invalidComponent); 357 if (delegate != null) { 358 delegate.addInvalidComponent(invalidComponent); 359 return; 360 } 361 Component validateRoot = 362 SwingUtilities.getValidateRoot(invalidComponent, true); 363 364 if (validateRoot == null) { 365 return; 366 } 367 368 /* Lazily create the invalidateComponents vector and add the 369 * validateRoot if it's not there already. If this validateRoot 370 * is already in the vector, we're done. 371 */ 372 if (invalidComponents == null) { 373 invalidComponents = new ArrayList<Component>(); 374 } 375 else { 376 int n = invalidComponents.size(); 377 for(int i = 0; i < n; i++) { 378 if(validateRoot == invalidComponents.get(i)) { 379 return; 380 } 381 } 382 } 383 invalidComponents.add(validateRoot); 384 385 // Queue a Runnable to invoke paintDirtyRegions and 386 // validateInvalidComponents. 387 scheduleProcessingRunnable(SunToolkit.targetToAppContext(invalidComponent)); 388 } 389 390 391 /** 392 * Remove a component from the list of invalid components. 393 * 394 * @param component a component 395 * @see #addInvalidComponent 396 */ 397 public synchronized void removeInvalidComponent(JComponent component) { 398 RepaintManager delegate = getDelegate(component); 399 if (delegate != null) { 400 delegate.removeInvalidComponent(component); 401 return; 402 } 403 if(invalidComponents != null) { 404 int index = invalidComponents.indexOf(component); 405 if(index != -1) { 406 invalidComponents.remove(index); 407 } 408 } 409 } 410 411 412 /** 413 * Add a component in the list of components that should be refreshed. 414 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i> 415 * will be unioned with the region that should be redrawn. 416 * 417 * @see JComponent#repaint 418 */ 419 @SuppressWarnings("deprecation") 420 private void addDirtyRegion0(Container c, int x, int y, int w, int h) { 421 /* Special cases we don't have to bother with. 422 */ 423 if ((w <= 0) || (h <= 0) || (c == null)) { 424 return; 425 } 426 427 if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) { 428 return; 429 } 430 431 if (extendDirtyRegion(c, x, y, w, h)) { 432 // Component was already marked as dirty, region has been 433 // extended, no need to continue. 434 return; 435 } 436 437 /* Make sure that c and all it ancestors (up to an Applet or 438 * Window) are visible. This loop has the same effect as 439 * checking c.isShowing() (and note that it's still possible 440 * that c is completely obscured by an opaque ancestor in 441 * the specified rectangle). 442 */ 443 Component root = null; 444 445 // Note: We can't synchronize around this, Frame.getExtendedState 446 // is synchronized so that if we were to synchronize around this 447 // it could lead to the possibility of getting locks out 448 // of order and deadlocking. 449 for (Container p = c; p != null; p = p.getParent()) { 450 if (!p.isVisible() || !p.isDisplayable()) { 451 return; 452 } 453 if ((p instanceof Window) || (p instanceof Applet)) { 454 // Iconified frames are still visible! 455 if (p instanceof Frame && 456 (((Frame)p).getExtendedState() & Frame.ICONIFIED) == 457 Frame.ICONIFIED) { 458 return; 459 } 460 root = p; 461 break; 462 } 463 } 464 465 if (root == null) return; 466 467 synchronized(this) { 468 if (extendDirtyRegion(c, x, y, w, h)) { 469 // In between last check and this check another thread 470 // queued up runnable, can bail here. 471 return; 472 } 473 dirtyComponents.put(c, new Rectangle(x, y, w, h)); 474 } 475 476 // Queue a Runnable to invoke paintDirtyRegions and 477 // validateInvalidComponents. 478 scheduleProcessingRunnable(SunToolkit.targetToAppContext(c)); 479 } 480 481 /** 482 * Add a component in the list of components that should be refreshed. 483 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i> 484 * will be unioned with the region that should be redrawn. 485 * 486 * @param c Component to repaint, null results in nothing happening. 487 * @param x X coordinate of the region to repaint 488 * @param y Y coordinate of the region to repaint 489 * @param w Width of the region to repaint 490 * @param h Height of the region to repaint 491 * @see JComponent#repaint 492 */ 493 public void addDirtyRegion(JComponent c, int x, int y, int w, int h) 494 { 495 RepaintManager delegate = getDelegate(c); 496 if (delegate != null) { 497 delegate.addDirtyRegion(c, x, y, w, h); 498 return; 499 } 500 addDirtyRegion0(c, x, y, w, h); 501 } 502 503 /** 504 * Adds <code>window</code> to the list of <code>Component</code>s that 505 * need to be repainted. 506 * 507 * @param window Window to repaint, null results in nothing happening. 508 * @param x X coordinate of the region to repaint 509 * @param y Y coordinate of the region to repaint 510 * @param w Width of the region to repaint 511 * @param h Height of the region to repaint 512 * @see JFrame#repaint 513 * @see JWindow#repaint 514 * @see JDialog#repaint 515 * @since 1.6 516 */ 517 public void addDirtyRegion(Window window, int x, int y, int w, int h) { 518 addDirtyRegion0(window, x, y, w, h); 519 } 520 521 /** 522 * Adds <code>applet</code> to the list of <code>Component</code>s that 523 * need to be repainted. 524 * 525 * @param applet Applet to repaint, null results in nothing happening. 526 * @param x X coordinate of the region to repaint 527 * @param y Y coordinate of the region to repaint 528 * @param w Width of the region to repaint 529 * @param h Height of the region to repaint 530 * @see JApplet#repaint 531 * @since 1.6 532 * 533 * @deprecated The Applet API is deprecated. See the 534 * <a href="../../java/applet/package-summary.html"> java.applet package 535 * documentation</a> for further information. 536 */ 537 @Deprecated(since = "9") 538 public void addDirtyRegion(Applet applet, int x, int y, int w, int h) { 539 addDirtyRegion0(applet, x, y, w, h); 540 } 541 542 @SuppressWarnings("deprecation") 543 void scheduleHeavyWeightPaints() { 544 Map<Container,Rectangle> hws; 545 546 synchronized(this) { 547 if (hwDirtyComponents.size() == 0) { 548 return; 549 } 550 hws = hwDirtyComponents; 551 hwDirtyComponents = new IdentityHashMap<Container,Rectangle>(); 552 } 553 for (Container hw : hws.keySet()) { 554 Rectangle dirty = hws.get(hw); 555 if (hw instanceof Window) { 556 addDirtyRegion((Window)hw, dirty.x, dirty.y, 557 dirty.width, dirty.height); 558 } 559 else if (hw instanceof Applet) { 560 addDirtyRegion((Applet)hw, dirty.x, dirty.y, 561 dirty.width, dirty.height); 562 } 563 else { // SwingHeavyWeight 564 addDirtyRegion0(hw, dirty.x, dirty.y, 565 dirty.width, dirty.height); 566 } 567 } 568 } 569 570 // 571 // This is called from the toolkit thread when a native expose is 572 // received. 573 // 574 void nativeAddDirtyRegion(AppContext appContext, Container c, 575 int x, int y, int w, int h) { 576 if (w > 0 && h > 0) { 577 synchronized(this) { 578 Rectangle dirty = hwDirtyComponents.get(c); 579 if (dirty == null) { 580 hwDirtyComponents.put(c, new Rectangle(x, y, w, h)); 581 } 582 else { 583 hwDirtyComponents.put(c, SwingUtilities.computeUnion( 584 x, y, w, h, dirty)); 585 } 586 } 587 scheduleProcessingRunnable(appContext); 588 } 589 } 590 591 // 592 // This is called from the toolkit thread when awt needs to run a 593 // Runnable before we paint. 594 // 595 void nativeQueueSurfaceDataRunnable(AppContext appContext, 596 final Component c, final Runnable r) 597 { 598 synchronized(this) { 599 if (runnableList == null) { 600 runnableList = new LinkedList<Runnable>(); 601 } 602 runnableList.add(new Runnable() { 603 public void run() { 604 AccessControlContext stack = AccessController.getContext(); 605 AccessControlContext acc = 606 AWTAccessor.getComponentAccessor().getAccessControlContext(c); 607 javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Void>() { 608 public Void run() { 609 r.run(); 610 return null; 611 } 612 }, stack, acc); 613 } 614 }); 615 } 616 scheduleProcessingRunnable(appContext); 617 } 618 619 /** 620 * Extends the dirty region for the specified component to include 621 * the new region. 622 * 623 * @return false if <code>c</code> is not yet marked dirty. 624 */ 625 private synchronized boolean extendDirtyRegion( 626 Component c, int x, int y, int w, int h) { 627 Rectangle r = dirtyComponents.get(c); 628 if (r != null) { 629 // A non-null r implies c is already marked as dirty, 630 // and that the parent is valid. Therefore we can 631 // just union the rect and bail. 632 SwingUtilities.computeUnion(x, y, w, h, r); 633 return true; 634 } 635 return false; 636 } 637 638 /** 639 * Return the current dirty region for a component. 640 * Return an empty rectangle if the component is not 641 * dirty. 642 * 643 * @param aComponent a component 644 * @return the region 645 */ 646 public Rectangle getDirtyRegion(JComponent aComponent) { 647 RepaintManager delegate = getDelegate(aComponent); 648 if (delegate != null) { 649 return delegate.getDirtyRegion(aComponent); 650 } 651 Rectangle r; 652 synchronized(this) { 653 r = dirtyComponents.get(aComponent); 654 } 655 if(r == null) 656 return new Rectangle(0,0,0,0); 657 else 658 return new Rectangle(r); 659 } 660 661 /** 662 * Mark a component completely dirty. <b>aComponent</b> will be 663 * completely painted during the next paintDirtyRegions() call. 664 * 665 * @param aComponent a component 666 */ 667 public void markCompletelyDirty(JComponent aComponent) { 668 RepaintManager delegate = getDelegate(aComponent); 669 if (delegate != null) { 670 delegate.markCompletelyDirty(aComponent); 671 return; 672 } 673 addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE); 674 } 675 676 /** 677 * Mark a component completely clean. <b>aComponent</b> will not 678 * get painted during the next paintDirtyRegions() call. 679 * 680 * @param aComponent a component 681 */ 682 public void markCompletelyClean(JComponent aComponent) { 683 RepaintManager delegate = getDelegate(aComponent); 684 if (delegate != null) { 685 delegate.markCompletelyClean(aComponent); 686 return; 687 } 688 synchronized(this) { 689 dirtyComponents.remove(aComponent); 690 } 691 } 692 693 /** 694 * Convenience method that returns true if <b>aComponent</b> will be completely 695 * painted during the next paintDirtyRegions(). If computing dirty regions is 696 * expensive for your component, use this method and avoid computing dirty region 697 * if it return true. 698 * 699 * @param aComponent a component 700 * @return {@code true} if <b>aComponent</b> will be completely 701 * painted during the next paintDirtyRegions(). 702 */ 703 public boolean isCompletelyDirty(JComponent aComponent) { 704 RepaintManager delegate = getDelegate(aComponent); 705 if (delegate != null) { 706 return delegate.isCompletelyDirty(aComponent); 707 } 708 Rectangle r; 709 710 r = getDirtyRegion(aComponent); 711 if(r.width == Integer.MAX_VALUE && 712 r.height == Integer.MAX_VALUE) 713 return true; 714 else 715 return false; 716 } 717 718 719 /** 720 * Validate all of the components that have been marked invalid. 721 * @see #addInvalidComponent 722 */ 723 public void validateInvalidComponents() { 724 final java.util.List<Component> ic; 725 synchronized(this) { 726 if (invalidComponents == null) { 727 return; 728 } 729 ic = invalidComponents; 730 invalidComponents = null; 731 } 732 int n = ic.size(); 733 for(int i = 0; i < n; i++) { 734 final Component c = ic.get(i); 735 AccessControlContext stack = AccessController.getContext(); 736 AccessControlContext acc = 737 AWTAccessor.getComponentAccessor().getAccessControlContext(c); 738 javaSecurityAccess.doIntersectionPrivilege( 739 new PrivilegedAction<Void>() { 740 public Void run() { 741 c.validate(); 742 return null; 743 } 744 }, stack, acc); 745 } 746 } 747 748 749 /** 750 * This is invoked to process paint requests. It's needed 751 * for backward compatibility in so far as RepaintManager would previously 752 * not see paint requests for top levels, so, we have to make sure 753 * a subclass correctly paints any dirty top levels. 754 */ 755 private void prePaintDirtyRegions() { 756 Map<Component,Rectangle> dirtyComponents; 757 java.util.List<Runnable> runnableList; 758 synchronized(this) { 759 dirtyComponents = this.dirtyComponents; 760 runnableList = this.runnableList; 761 this.runnableList = null; 762 } 763 if (runnableList != null) { 764 for (Runnable runnable : runnableList) { 765 runnable.run(); 766 } 767 } 768 paintDirtyRegions(); 769 if (dirtyComponents.size() > 0) { 770 // This'll only happen if a subclass isn't correctly dealing 771 // with toplevels. 772 paintDirtyRegions(dirtyComponents); 773 } 774 } 775 776 private void updateWindows(Map<Component,Rectangle> dirtyComponents) { 777 Toolkit toolkit = Toolkit.getDefaultToolkit(); 778 if (!(toolkit instanceof SunToolkit && 779 ((SunToolkit)toolkit).needUpdateWindow())) 780 { 781 return; 782 } 783 784 Set<Window> windows = new HashSet<Window>(); 785 Set<Component> dirtyComps = dirtyComponents.keySet(); 786 for (Iterator<Component> it = dirtyComps.iterator(); it.hasNext();) { 787 Component dirty = it.next(); 788 Window window = dirty instanceof Window ? 789 (Window)dirty : 790 SwingUtilities.getWindowAncestor(dirty); 791 if (window != null && 792 !window.isOpaque()) 793 { 794 windows.add(window); 795 } 796 } 797 798 for (Window window : windows) { 799 AWTAccessor.getWindowAccessor().updateWindow(window); 800 } 801 } 802 803 boolean isPainting() { 804 return painting; 805 } 806 807 /** 808 * Paint all of the components that have been marked dirty. 809 * 810 * @see #addDirtyRegion 811 */ 812 public void paintDirtyRegions() { 813 synchronized(this) { // swap for thread safety 814 Map<Component,Rectangle> tmp = tmpDirtyComponents; 815 tmpDirtyComponents = dirtyComponents; 816 dirtyComponents = tmp; 817 dirtyComponents.clear(); 818 } 819 paintDirtyRegions(tmpDirtyComponents); 820 } 821 822 private void paintDirtyRegions( 823 final Map<Component,Rectangle> tmpDirtyComponents) 824 { 825 if (tmpDirtyComponents.isEmpty()) { 826 return; 827 } 828 829 final java.util.List<Component> roots = 830 new ArrayList<Component>(tmpDirtyComponents.size()); 831 for (Component dirty : tmpDirtyComponents.keySet()) { 832 collectDirtyComponents(tmpDirtyComponents, dirty, roots); 833 } 834 835 final AtomicInteger count = new AtomicInteger(roots.size()); 836 painting = true; 837 try { 838 for (int j=0 ; j < count.get(); j++) { 839 final int i = j; 840 final Component dirtyComponent = roots.get(j); 841 AccessControlContext stack = AccessController.getContext(); 842 AccessControlContext acc = 843 AWTAccessor.getComponentAccessor().getAccessControlContext(dirtyComponent); 844 javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Void>() { 845 public Void run() { 846 Rectangle rect = tmpDirtyComponents.get(dirtyComponent); 847 // Sometimes when RepaintManager is changed during the painting 848 // we may get null here, see #6995769 for details 849 if (rect == null) { 850 return null; 851 } 852 853 int localBoundsH = dirtyComponent.getHeight(); 854 int localBoundsW = dirtyComponent.getWidth(); 855 SwingUtilities.computeIntersection(0, 856 0, 857 localBoundsW, 858 localBoundsH, 859 rect); 860 if (dirtyComponent instanceof JComponent) { 861 ((JComponent)dirtyComponent).paintImmediately( 862 rect.x,rect.y,rect.width, rect.height); 863 } 864 else if (dirtyComponent.isShowing()) { 865 Graphics g = JComponent.safelyGetGraphics( 866 dirtyComponent, dirtyComponent); 867 // If the Graphics goes away, it means someone disposed of 868 // the window, don't do anything. 869 if (g != null) { 870 g.setClip(rect.x, rect.y, rect.width, rect.height); 871 try { 872 dirtyComponent.paint(g); 873 } finally { 874 g.dispose(); 875 } 876 } 877 } 878 // If the repaintRoot has been set, service it now and 879 // remove any components that are children of repaintRoot. 880 if (repaintRoot != null) { 881 adjustRoots(repaintRoot, roots, i + 1); 882 count.set(roots.size()); 883 paintManager.isRepaintingRoot = true; 884 repaintRoot.paintImmediately(0, 0, repaintRoot.getWidth(), 885 repaintRoot.getHeight()); 886 paintManager.isRepaintingRoot = false; 887 // Only service repaintRoot once. 888 repaintRoot = null; 889 } 890 891 return null; 892 } 893 }, stack, acc); 894 } 895 } finally { 896 painting = false; 897 } 898 899 updateWindows(tmpDirtyComponents); 900 901 tmpDirtyComponents.clear(); 902 } 903 904 905 /** 906 * Removes any components from roots that are children of 907 * root. 908 */ 909 private void adjustRoots(JComponent root, 910 java.util.List<Component> roots, int index) { 911 for (int i = roots.size() - 1; i >= index; i--) { 912 Component c = roots.get(i); 913 for(;;) { 914 if (c == root || c == null || !(c instanceof JComponent)) { 915 break; 916 } 917 c = c.getParent(); 918 } 919 if (c == root) { 920 roots.remove(i); 921 } 922 } 923 } 924 925 Rectangle tmp = new Rectangle(); 926 927 void collectDirtyComponents(Map<Component,Rectangle> dirtyComponents, 928 Component dirtyComponent, 929 java.util.List<Component> roots) { 930 int dx, dy, rootDx, rootDy; 931 Component component, rootDirtyComponent,parent; 932 Rectangle cBounds; 933 934 // Find the highest parent which is dirty. When we get out of this 935 // rootDx and rootDy will contain the translation from the 936 // rootDirtyComponent's coordinate system to the coordinates of the 937 // original dirty component. The tmp Rect is also used to compute the 938 // visible portion of the dirtyRect. 939 940 component = rootDirtyComponent = dirtyComponent; 941 942 int x = dirtyComponent.getX(); 943 int y = dirtyComponent.getY(); 944 int w = dirtyComponent.getWidth(); 945 int h = dirtyComponent.getHeight(); 946 947 dx = rootDx = 0; 948 dy = rootDy = 0; 949 tmp.setBounds(dirtyComponents.get(dirtyComponent)); 950 951 // System.out.println("Collect dirty component for bound " + tmp + 952 // "component bounds is " + cBounds);; 953 SwingUtilities.computeIntersection(0,0,w,h,tmp); 954 955 if (tmp.isEmpty()) { 956 // System.out.println("Empty 1"); 957 return; 958 } 959 960 for(;;) { 961 if(!(component instanceof JComponent)) 962 break; 963 964 parent = component.getParent(); 965 if(parent == null) 966 break; 967 968 component = parent; 969 970 dx += x; 971 dy += y; 972 tmp.setLocation(tmp.x + x, tmp.y + y); 973 974 x = component.getX(); 975 y = component.getY(); 976 w = component.getWidth(); 977 h = component.getHeight(); 978 tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp); 979 980 if (tmp.isEmpty()) { 981 // System.out.println("Empty 2"); 982 return; 983 } 984 985 if (dirtyComponents.get(component) != null) { 986 rootDirtyComponent = component; 987 rootDx = dx; 988 rootDy = dy; 989 } 990 } 991 992 if (dirtyComponent != rootDirtyComponent) { 993 Rectangle r; 994 tmp.setLocation(tmp.x + rootDx - dx, 995 tmp.y + rootDy - dy); 996 r = dirtyComponents.get(rootDirtyComponent); 997 SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r); 998 } 999 1000 // If we haven't seen this root before, then we need to add it to the 1001 // list of root dirty Views. 1002 1003 if (!roots.contains(rootDirtyComponent)) 1004 roots.add(rootDirtyComponent); 1005 } 1006 1007 1008 /** 1009 * Returns a string that displays and identifies this 1010 * object's properties. 1011 * 1012 * @return a String representation of this object 1013 */ 1014 public synchronized String toString() { 1015 StringBuilder sb = new StringBuilder(); 1016 if(dirtyComponents != null) 1017 sb.append("" + dirtyComponents); 1018 return sb.toString(); 1019 } 1020 1021 1022 /** 1023 * Return the offscreen buffer that should be used as a double buffer with 1024 * the component <code>c</code>. 1025 * By default there is a double buffer per RepaintManager. 1026 * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code> 1027 * This happens when the maximum double buffer size as been set for the receiving 1028 * repaint manager. 1029 * 1030 * @param c the component 1031 * @param proposedWidth the width of the buffer 1032 * @param proposedHeight the height of the buffer 1033 * 1034 * @return the image 1035 */ 1036 public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) { 1037 RepaintManager delegate = getDelegate(c); 1038 if (delegate != null) { 1039 return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight); 1040 } 1041 return _getOffscreenBuffer(c, proposedWidth, proposedHeight); 1042 } 1043 1044 /** 1045 * Return a volatile offscreen buffer that should be used as a 1046 * double buffer with the specified component <code>c</code>. 1047 * The image returned will be an instance of VolatileImage, or null 1048 * if a VolatileImage object could not be instantiated. 1049 * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>. 1050 * This happens when the maximum double buffer size has been set for this 1051 * repaint manager. 1052 * 1053 * @param c the component 1054 * @param proposedWidth the width of the buffer 1055 * @param proposedHeight the height of the buffer 1056 * 1057 * @return the volatile image 1058 * @see java.awt.image.VolatileImage 1059 * @since 1.4 1060 */ 1061 public Image getVolatileOffscreenBuffer(Component c, 1062 int proposedWidth,int proposedHeight) { 1063 RepaintManager delegate = getDelegate(c); 1064 if (delegate != null) { 1065 return delegate.getVolatileOffscreenBuffer(c, proposedWidth, 1066 proposedHeight); 1067 } 1068 1069 // If the window is non-opaque, it's double-buffered at peer's level 1070 Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c); 1071 if (!w.isOpaque()) { 1072 Toolkit tk = Toolkit.getDefaultToolkit(); 1073 if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) { 1074 return null; 1075 } 1076 } 1077 1078 GraphicsConfiguration config = c.getGraphicsConfiguration(); 1079 if (config == null) { 1080 config = GraphicsEnvironment.getLocalGraphicsEnvironment(). 1081 getDefaultScreenDevice().getDefaultConfiguration(); 1082 } 1083 Dimension maxSize = getDoubleBufferMaximumSize(); 1084 int width = proposedWidth < 1 ? 1 : 1085 (proposedWidth > maxSize.width? maxSize.width : proposedWidth); 1086 int height = proposedHeight < 1 ? 1 : 1087 (proposedHeight > maxSize.height? maxSize.height : proposedHeight); 1088 VolatileImage image = volatileMap.get(config); 1089 if (image == null || image.getWidth() < width || 1090 image.getHeight() < height) { 1091 if (image != null) { 1092 image.flush(); 1093 } 1094 image = config.createCompatibleVolatileImage(width, height, 1095 volatileBufferType); 1096 volatileMap.put(config, image); 1097 } 1098 return image; 1099 } 1100 1101 private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { 1102 Dimension maxSize = getDoubleBufferMaximumSize(); 1103 DoubleBufferInfo doubleBuffer; 1104 int width, height; 1105 1106 // If the window is non-opaque, it's double-buffered at peer's level 1107 Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c); 1108 if (!w.isOpaque()) { 1109 Toolkit tk = Toolkit.getDefaultToolkit(); 1110 if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) { 1111 return null; 1112 } 1113 } 1114 1115 if (standardDoubleBuffer == null) { 1116 standardDoubleBuffer = new DoubleBufferInfo(); 1117 } 1118 doubleBuffer = standardDoubleBuffer; 1119 1120 width = proposedWidth < 1? 1 : 1121 (proposedWidth > maxSize.width? maxSize.width : proposedWidth); 1122 height = proposedHeight < 1? 1 : 1123 (proposedHeight > maxSize.height? maxSize.height : proposedHeight); 1124 1125 if (doubleBuffer.needsReset || (doubleBuffer.image != null && 1126 (doubleBuffer.size.width < width || 1127 doubleBuffer.size.height < height))) { 1128 doubleBuffer.needsReset = false; 1129 if (doubleBuffer.image != null) { 1130 doubleBuffer.image.flush(); 1131 doubleBuffer.image = null; 1132 } 1133 width = Math.max(doubleBuffer.size.width, width); 1134 height = Math.max(doubleBuffer.size.height, height); 1135 } 1136 1137 Image result = doubleBuffer.image; 1138 1139 if (doubleBuffer.image == null) { 1140 result = c.createImage(width , height); 1141 doubleBuffer.size = new Dimension(width, height); 1142 if (c instanceof JComponent) { 1143 ((JComponent)c).setCreatedDoubleBuffer(true); 1144 doubleBuffer.image = result; 1145 } 1146 // JComponent will inform us when it is no longer valid 1147 // (via removeNotify) we have no such hook to other components, 1148 // therefore we don't keep a ref to the Component 1149 // (indirectly through the Image) by stashing the image. 1150 } 1151 return result; 1152 } 1153 1154 1155 /** 1156 * Set the maximum double buffer size. 1157 * 1158 * @param d the dimension 1159 */ 1160 public void setDoubleBufferMaximumSize(Dimension d) { 1161 doubleBufferMaxSize = d; 1162 if (doubleBufferMaxSize == null) { 1163 clearImages(); 1164 } else { 1165 clearImages(d.width, d.height); 1166 } 1167 } 1168 1169 private void clearImages() { 1170 clearImages(0, 0); 1171 } 1172 1173 private void clearImages(int width, int height) { 1174 if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) { 1175 if (standardDoubleBuffer.image.getWidth(null) > width || 1176 standardDoubleBuffer.image.getHeight(null) > height) { 1177 standardDoubleBuffer.image.flush(); 1178 standardDoubleBuffer.image = null; 1179 } 1180 } 1181 // Clear out the VolatileImages 1182 Iterator<GraphicsConfiguration> gcs = volatileMap.keySet().iterator(); 1183 while (gcs.hasNext()) { 1184 GraphicsConfiguration gc = gcs.next(); 1185 VolatileImage image = volatileMap.get(gc); 1186 if (image.getWidth() > width || image.getHeight() > height) { 1187 image.flush(); 1188 gcs.remove(); 1189 } 1190 } 1191 } 1192 1193 /** 1194 * Returns the maximum double buffer size. 1195 * 1196 * @return a Dimension object representing the maximum size 1197 */ 1198 public Dimension getDoubleBufferMaximumSize() { 1199 if (doubleBufferMaxSize == null) { 1200 try { 1201 Rectangle virtualBounds = new Rectangle(); 1202 GraphicsEnvironment ge = GraphicsEnvironment. 1203 getLocalGraphicsEnvironment(); 1204 for (GraphicsDevice gd : ge.getScreenDevices()) { 1205 GraphicsConfiguration gc = gd.getDefaultConfiguration(); 1206 virtualBounds = virtualBounds.union(gc.getBounds()); 1207 } 1208 doubleBufferMaxSize = new Dimension(virtualBounds.width, 1209 virtualBounds.height); 1210 } catch (HeadlessException e) { 1211 doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 1212 } 1213 } 1214 if (doubleBufferMaxSize != null) { 1215 System.err.println("Buffer size " + doubleBufferMaxSize.width + ", " + doubleBufferMaxSize.height); 1216 } 1217 return doubleBufferMaxSize; 1218 } 1219 1220 /** 1221 * Enables or disables double buffering in this RepaintManager. 1222 * CAUTION: The default value for this property is set for optimal 1223 * paint performance on the given platform and it is not recommended 1224 * that programs modify this property directly. 1225 * 1226 * @param aFlag true to activate double buffering 1227 * @see #isDoubleBufferingEnabled 1228 */ 1229 public void setDoubleBufferingEnabled(boolean aFlag) { 1230 doubleBufferingEnabled = aFlag; 1231 PaintManager paintManager = getPaintManager(); 1232 if (!aFlag && paintManager.getClass() != PaintManager.class) { 1233 setPaintManager(new PaintManager()); 1234 } 1235 } 1236 1237 /** 1238 * Returns true if this RepaintManager is double buffered. 1239 * The default value for this property may vary from platform 1240 * to platform. On platforms where native double buffering 1241 * is supported in the AWT, the default value will be <code>false</code> 1242 * to avoid unnecessary buffering in Swing. 1243 * On platforms where native double buffering is not supported, 1244 * the default value will be <code>true</code>. 1245 * 1246 * @return true if this object is double buffered 1247 */ 1248 public boolean isDoubleBufferingEnabled() { 1249 return doubleBufferingEnabled; 1250 } 1251 1252 /** 1253 * This resets the double buffer. Actually, it marks the double buffer 1254 * as invalid, the double buffer will then be recreated on the next 1255 * invocation of getOffscreenBuffer. 1256 */ 1257 void resetDoubleBuffer() { 1258 if (standardDoubleBuffer != null) { 1259 standardDoubleBuffer.needsReset = true; 1260 } 1261 } 1262 1263 /** 1264 * This resets the volatile double buffer. 1265 */ 1266 void resetVolatileDoubleBuffer(GraphicsConfiguration gc) { 1267 Image image = volatileMap.remove(gc); 1268 if (image != null) { 1269 image.flush(); 1270 } 1271 } 1272 1273 /** 1274 * Returns true if we should use the <code>Image</code> returned 1275 * from <code>getVolatileOffscreenBuffer</code> to do double buffering. 1276 */ 1277 boolean useVolatileDoubleBuffer() { 1278 return volatileImageBufferEnabled; 1279 } 1280 1281 /** 1282 * Returns true if the current thread is the thread painting. This 1283 * will return false if no threads are painting. 1284 */ 1285 private synchronized boolean isPaintingThread() { 1286 return (Thread.currentThread() == paintThread); 1287 } 1288 // 1289 // Paint methods. You very, VERY rarely need to invoke these. 1290 // They are invoked directly from JComponent's painting code and 1291 // when painting happens outside the normal flow: DefaultDesktopManager 1292 // and JViewport. If you end up needing these methods in other places be 1293 // careful that you don't get stuck in a paint loop. 1294 // 1295 1296 /** 1297 * Paints a region of a component 1298 * 1299 * @param paintingComponent Component to paint 1300 * @param bufferComponent Component to obtain buffer for 1301 * @param g Graphics to paint to 1302 * @param x X-coordinate 1303 * @param y Y-coordinate 1304 * @param w Width 1305 * @param h Height 1306 */ 1307 void paint(JComponent paintingComponent, 1308 JComponent bufferComponent, Graphics g, 1309 int x, int y, int w, int h) { 1310 PaintManager paintManager = getPaintManager(); 1311 if (!isPaintingThread()) { 1312 // We're painting to two threads at once. PaintManager deals 1313 // with this a bit better than BufferStrategyPaintManager, use 1314 // it to avoid possible exceptions/corruption. 1315 if (paintManager.getClass() != PaintManager.class) { 1316 paintManager = new PaintManager(); 1317 paintManager.repaintManager = this; 1318 } 1319 } 1320 if (!paintManager.paint(paintingComponent, bufferComponent, g, 1321 x, y, w, h)) { 1322 g.setClip(x, y, w, h); 1323 paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h); 1324 } 1325 } 1326 1327 /** 1328 * Does a copy area on the specified region. 1329 * 1330 * @param clip Whether or not the copyArea needs to be clipped to the 1331 * Component's bounds. 1332 */ 1333 void copyArea(JComponent c, Graphics g, int x, int y, int w, int h, 1334 int deltaX, int deltaY, boolean clip) { 1335 getPaintManager().copyArea(c, g, x, y, w, h, deltaX, deltaY, clip); 1336 } 1337 1338 private java.util.List<RepaintListener> repaintListeners = new ArrayList<>(1); 1339 1340 private void addRepaintListener(RepaintListener l) { 1341 repaintListeners.add(l); 1342 } 1343 1344 private void removeRepaintListener(RepaintListener l) { 1345 repaintListeners.remove(l); 1346 } 1347 1348 /** 1349 * Notify the attached repaint listeners that an area of the {@code c} component 1350 * has been immediately repainted, that is without scheduling a repaint runnable, 1351 * due to performing a "blit" (via calling the {@code copyArea} method). 1352 * 1353 * @param c the component 1354 * @param x the x coordinate of the area 1355 * @param y the y coordinate of the area 1356 * @param w the width of the area 1357 * @param h the height of the area 1358 */ 1359 void notifyRepaintPerformed(JComponent c, int x, int y, int w, int h) { 1360 for (RepaintListener l : repaintListeners) { 1361 l.repaintPerformed(c, x, y, w, h); 1362 } 1363 } 1364 1365 /** 1366 * Invoked prior to any paint/copyArea method calls. This will 1367 * be followed by an invocation of <code>endPaint</code>. 1368 * <b>WARNING</b>: Callers of this method need to wrap the call 1369 * in a <code>try/finally</code>, otherwise if an exception is thrown 1370 * during the course of painting the RepaintManager may 1371 * be left in a state in which the screen is not updated, eg: 1372 * <pre> 1373 * repaintManager.beginPaint(); 1374 * try { 1375 * repaintManager.paint(...); 1376 * } finally { 1377 * repaintManager.endPaint(); 1378 * } 1379 * </pre> 1380 */ 1381 void beginPaint() { 1382 boolean multiThreadedPaint = false; 1383 int paintDepth; 1384 Thread currentThread = Thread.currentThread(); 1385 synchronized(this) { 1386 paintDepth = this.paintDepth; 1387 if (paintThread == null || currentThread == paintThread) { 1388 paintThread = currentThread; 1389 this.paintDepth++; 1390 } else { 1391 multiThreadedPaint = true; 1392 } 1393 } 1394 if (!multiThreadedPaint && paintDepth == 0) { 1395 getPaintManager().beginPaint(); 1396 } 1397 } 1398 1399 /** 1400 * Invoked after <code>beginPaint</code> has been invoked. 1401 */ 1402 void endPaint() { 1403 if (isPaintingThread()) { 1404 PaintManager paintManager = null; 1405 synchronized(this) { 1406 if (--paintDepth == 0) { 1407 paintManager = getPaintManager(); 1408 } 1409 } 1410 if (paintManager != null) { 1411 paintManager.endPaint(); 1412 synchronized(this) { 1413 paintThread = null; 1414 } 1415 } 1416 } 1417 } 1418 1419 /** 1420 * If possible this will show a previously rendered portion of 1421 * a Component. If successful, this will return true, otherwise false. 1422 * <p> 1423 * WARNING: This method is invoked from the native toolkit thread, be 1424 * very careful as to what methods this invokes! 1425 */ 1426 boolean show(Container c, int x, int y, int w, int h) { 1427 return getPaintManager().show(c, x, y, w, h); 1428 } 1429 1430 /** 1431 * Invoked when the doubleBuffered or useTrueDoubleBuffering 1432 * properties of a JRootPane change. This may come in on any thread. 1433 */ 1434 void doubleBufferingChanged(JRootPane rootPane) { 1435 getPaintManager().doubleBufferingChanged(rootPane); 1436 } 1437 1438 /** 1439 * Sets the <code>PaintManager</code> that is used to handle all 1440 * double buffered painting. 1441 * 1442 * @param paintManager The PaintManager to use. Passing in null indicates 1443 * the fallback PaintManager should be used. 1444 */ 1445 void setPaintManager(PaintManager paintManager) { 1446 if (paintManager == null) { 1447 paintManager = new PaintManager(); 1448 } 1449 PaintManager oldPaintManager; 1450 synchronized(this) { 1451 oldPaintManager = this.paintManager; 1452 this.paintManager = paintManager; 1453 paintManager.repaintManager = this; 1454 } 1455 if (oldPaintManager != null) { 1456 oldPaintManager.dispose(); 1457 } 1458 } 1459 1460 private synchronized PaintManager getPaintManager() { 1461 if (paintManager == null) { 1462 PaintManager paintManager = null; 1463 if (doubleBufferingEnabled && !nativeDoubleBuffering) { 1464 switch (bufferStrategyType) { 1465 case BUFFER_STRATEGY_NOT_SPECIFIED: 1466 Toolkit tk = Toolkit.getDefaultToolkit(); 1467 if (tk instanceof SunToolkit) { 1468 SunToolkit stk = (SunToolkit) tk; 1469 if (stk.useBufferPerWindow()) { 1470 paintManager = new BufferStrategyPaintManager(); 1471 } 1472 } 1473 break; 1474 case BUFFER_STRATEGY_SPECIFIED_ON: 1475 paintManager = new BufferStrategyPaintManager(); 1476 break; 1477 default: 1478 break; 1479 } 1480 } 1481 // null case handled in setPaintManager 1482 setPaintManager(paintManager); 1483 } 1484 return paintManager; 1485 } 1486 1487 private void scheduleProcessingRunnable(AppContext context) { 1488 if (processingRunnable.markPending()) { 1489 Toolkit tk = Toolkit.getDefaultToolkit(); 1490 if (tk instanceof SunToolkit) { 1491 SunToolkit.getSystemEventQueueImplPP(context). 1492 postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), 1493 processingRunnable)); 1494 } else { 1495 Toolkit.getDefaultToolkit().getSystemEventQueue(). 1496 postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), 1497 processingRunnable)); 1498 } 1499 } 1500 } 1501 1502 1503 /** 1504 * PaintManager is used to handle all double buffered painting for 1505 * Swing. Subclasses should call back into the JComponent method 1506 * <code>paintToOffscreen</code> to handle the actual painting. 1507 */ 1508 static class PaintManager { 1509 /** 1510 * RepaintManager the PaintManager has been installed on. 1511 */ 1512 protected RepaintManager repaintManager; 1513 boolean isRepaintingRoot; 1514 1515 /** 1516 * Paints a region of a component 1517 * 1518 * @param paintingComponent Component to paint 1519 * @param bufferComponent Component to obtain buffer for 1520 * @param g Graphics to paint to 1521 * @param x X-coordinate 1522 * @param y Y-coordinate 1523 * @param w Width 1524 * @param h Height 1525 * @return true if painting was successful. 1526 */ 1527 public boolean paint(JComponent paintingComponent, 1528 JComponent bufferComponent, Graphics g, 1529 int x, int y, int w, int h) { 1530 // First attempt to use VolatileImage buffer for performance. 1531 // If this fails (which should rarely occur), fallback to a 1532 // standard Image buffer. 1533 boolean paintCompleted = false; 1534 Image offscreen; 1535 int sw = w + 1; 1536 int sh = h + 1; 1537 1538 if (repaintManager.useVolatileDoubleBuffer() && 1539 (offscreen = getValidImage(repaintManager. 1540 getVolatileOffscreenBuffer(bufferComponent, sw, sh))) != null) { 1541 VolatileImage vImage = (java.awt.image.VolatileImage)offscreen; 1542 GraphicsConfiguration gc = bufferComponent. 1543 getGraphicsConfiguration(); 1544 for (int i = 0; !paintCompleted && 1545 i < RepaintManager.VOLATILE_LOOP_MAX; i++) { 1546 if (vImage.validate(gc) == 1547 VolatileImage.IMAGE_INCOMPATIBLE) { 1548 repaintManager.resetVolatileDoubleBuffer(gc); 1549 offscreen = repaintManager.getVolatileOffscreenBuffer( 1550 bufferComponent, sw, sh); 1551 vImage = (java.awt.image.VolatileImage)offscreen; 1552 } 1553 paintDoubleBuffered(paintingComponent, vImage, g, x, y, 1554 w, h); 1555 paintCompleted = !vImage.contentsLost(); 1556 } 1557 } 1558 // VolatileImage painting loop failed, fallback to regular 1559 // offscreen buffer 1560 if (!paintCompleted && (offscreen = getValidImage( 1561 repaintManager.getOffscreenBuffer( 1562 bufferComponent, w, h))) != null) { 1563 paintDoubleBuffered(paintingComponent, offscreen, g, x, y, w, 1564 h); 1565 paintCompleted = true; 1566 } 1567 return paintCompleted; 1568 } 1569 1570 /** 1571 * Does a copy area on the specified region. 1572 */ 1573 public void copyArea(JComponent c, Graphics g, int x, int y, int w, 1574 int h, int deltaX, int deltaY, boolean clip) { 1575 g.copyArea(x, y, w, h, deltaX, deltaY); 1576 } 1577 1578 /** 1579 * Invoked prior to any calls to paint or copyArea. 1580 */ 1581 public void beginPaint() { 1582 } 1583 1584 /** 1585 * Invoked to indicate painting has been completed. 1586 */ 1587 public void endPaint() { 1588 } 1589 1590 /** 1591 * Shows a region of a previously rendered component. This 1592 * will return true if successful, false otherwise. The default 1593 * implementation returns false. 1594 */ 1595 public boolean show(Container c, int x, int y, int w, int h) { 1596 return false; 1597 } 1598 1599 /** 1600 * Invoked when the doubleBuffered or useTrueDoubleBuffering 1601 * properties of a JRootPane change. This may come in on any thread. 1602 */ 1603 public void doubleBufferingChanged(JRootPane rootPane) { 1604 } 1605 1606 /** 1607 * Paints a portion of a component to an offscreen buffer. 1608 */ 1609 protected void paintDoubleBuffered(JComponent c, Image image, 1610 Graphics g, int clipX, int clipY, 1611 int clipW, int clipH) { 1612 if (image instanceof VolatileImage && isPixelsCopying(c, g)) { 1613 paintDoubleBufferedFPScales(c, image, g, clipX, clipY, clipW, clipH); 1614 } else { 1615 paintDoubleBufferedImpl(c, image, g, clipX, clipY, clipW, clipH); 1616 } 1617 } 1618 1619 private void paintDoubleBufferedImpl(JComponent c, Image image, 1620 Graphics g, int clipX, int clipY, 1621 int clipW, int clipH) { 1622 Graphics osg = image.getGraphics(); 1623 int bw = Math.min(clipW, image.getWidth(null)); 1624 int bh = Math.min(clipH, image.getHeight(null)); 1625 int x,y,maxx,maxy; 1626 1627 try { 1628 for(x = clipX, maxx = clipX+clipW; x < maxx ; x += bw ) { 1629 for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) { 1630 osg.translate(-x, -y); 1631 osg.setClip(x,y,bw,bh); 1632 if (volatileBufferType != Transparency.OPAQUE 1633 && osg instanceof Graphics2D) { 1634 final Graphics2D g2d = (Graphics2D) osg; 1635 final Color oldBg = g2d.getBackground(); 1636 g2d.setBackground(c.getBackground()); 1637 g2d.clearRect(x, y, bw, bh); 1638 g2d.setBackground(oldBg); 1639 } 1640 c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy); 1641 g.setClip(x, y, bw, bh); 1642 if (volatileBufferType != Transparency.OPAQUE 1643 && g instanceof Graphics2D) { 1644 final Graphics2D g2d = (Graphics2D) g; 1645 final Composite oldComposite = g2d.getComposite(); 1646 g2d.setComposite(AlphaComposite.Src); 1647 g2d.drawImage(image, x, y, c); 1648 g2d.setComposite(oldComposite); 1649 } else { 1650 g.drawImage(image, x, y, c); 1651 } 1652 osg.translate(x, y); 1653 } 1654 } 1655 } finally { 1656 osg.dispose(); 1657 } 1658 } 1659 1660 private void paintDoubleBufferedFPScales(JComponent c, Image image, 1661 Graphics g, int clipX, int clipY, 1662 int clipW, int clipH) { 1663 Graphics osg = image.getGraphics(); 1664 Graphics2D g2d = (Graphics2D) g; 1665 Graphics2D osg2d = (Graphics2D) osg; 1666 1667 AffineTransform identity = new AffineTransform(); 1668 int bw = Math.min(clipW, image.getWidth(null)); 1669 int bh = Math.min(clipH, image.getHeight(null)); 1670 int x, y, maxx, maxy; 1671 1672 AffineTransform tx = g2d.getTransform(); 1673 double scaleX = tx.getScaleX(); 1674 double scaleY = tx.getScaleY(); 1675 double trX = tx.getTranslateX(); 1676 double trY = tx.getTranslateY(); 1677 1678 boolean translucent = volatileBufferType != Transparency.OPAQUE; 1679 Composite oldComposite = g2d.getComposite(); 1680 1681 try { 1682 for (x = clipX, maxx = clipX + clipW; x < maxx; x += bw) { 1683 for (y = clipY, maxy = clipY + clipH; y < maxy; y += bh) { 1684 1685 // draw x, y, bw, bh 1686 int pixelx1 = Region.clipRound(x * scaleX + trX); 1687 int pixely1 = Region.clipRound(y * scaleY + trY); 1688 int pixelx2 = Region.clipRound((x + bw) * scaleX + trX); 1689 int pixely2 = Region.clipRound((y + bh) * scaleY + trY); 1690 int pixelw = pixelx2 - pixelx1; 1691 int pixelh = pixely2 - pixely1; 1692 1693 osg2d.setTransform(identity); 1694 if (translucent) { 1695 final Color oldBg = g2d.getBackground(); 1696 g2d.setBackground(c.getBackground()); 1697 g2d.clearRect(pixelx1, pixely1, pixelw, pixelh); 1698 g2d.setBackground(oldBg); 1699 } 1700 1701 osg2d.setClip(0, 0, pixelw, pixelh); 1702 osg2d.translate(trX - pixelx1, trY - pixely1); 1703 osg2d.scale(scaleX, scaleY); 1704 c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy); 1705 1706 g2d.setTransform(identity); 1707 g2d.setClip(pixelx1, pixely1, pixelw, pixelh); 1708 AffineTransform stx = new AffineTransform(); 1709 stx.translate(pixelx1, pixely1); 1710 stx.scale(scaleX, scaleY); 1711 g2d.setTransform(stx); 1712 1713 if (translucent) { 1714 g2d.setComposite(AlphaComposite.Src); 1715 } 1716 1717 g2d.drawImage(image, 0, 0, c); 1718 1719 if (translucent) { 1720 g2d.setComposite(oldComposite); 1721 } 1722 g2d.setTransform(tx); 1723 } 1724 } 1725 } finally { 1726 osg.dispose(); 1727 } 1728 } 1729 1730 /** 1731 * If <code>image</code> is non-null with a positive size it 1732 * is returned, otherwise null is returned. 1733 */ 1734 private Image getValidImage(Image image) { 1735 if (image != null && image.getWidth(null) > 0 && 1736 image.getHeight(null) > 0) { 1737 return image; 1738 } 1739 return null; 1740 } 1741 1742 /** 1743 * Schedules a repaint for the specified component. This differs 1744 * from <code>root.repaint</code> in that if the RepaintManager is 1745 * currently processing paint requests it'll process this request 1746 * with the current set of requests. 1747 */ 1748 protected void repaintRoot(JComponent root) { 1749 assert (repaintManager.repaintRoot == null); 1750 if (repaintManager.painting) { 1751 repaintManager.repaintRoot = root; 1752 } 1753 else { 1754 root.repaint(); 1755 } 1756 } 1757 1758 /** 1759 * Returns true if the component being painted is the root component 1760 * that was previously passed to <code>repaintRoot</code>. 1761 */ 1762 protected boolean isRepaintingRoot() { 1763 return isRepaintingRoot; 1764 } 1765 1766 /** 1767 * Cleans up any state. After invoked the PaintManager will no 1768 * longer be used anymore. 1769 */ 1770 protected void dispose() { 1771 } 1772 1773 private boolean isPixelsCopying(JComponent c, Graphics g) { 1774 1775 AffineTransform tx = getTransform(g); 1776 GraphicsConfiguration gc = c.getGraphicsConfiguration(); 1777 1778 if (tx == null || gc == null 1779 || !SwingUtilities2.isFloatingPointScale(tx)) { 1780 return false; 1781 } 1782 1783 AffineTransform gcTx = gc.getDefaultTransform(); 1784 1785 return gcTx.getScaleX() == tx.getScaleX() 1786 && gcTx.getScaleY() == tx.getScaleY(); 1787 } 1788 1789 private static AffineTransform getTransform(Graphics g) { 1790 if (g instanceof SunGraphics2D) { 1791 return ((SunGraphics2D) g).transform; 1792 } else if (g instanceof Graphics2D) { 1793 return ((Graphics2D) g).getTransform(); 1794 } 1795 return null; 1796 } 1797 } 1798 1799 private class DoubleBufferInfo { 1800 public Image image; 1801 public Dimension size; 1802 public boolean needsReset = false; 1803 } 1804 1805 1806 /** 1807 * Listener installed to detect display changes. When display changes, 1808 * schedules a callback to notify all RepaintManagers of the display 1809 * changes. Only one DisplayChangedHandler is ever installed. The 1810 * singleton instance will schedule notification for all AppContexts. 1811 */ 1812 private static final class DisplayChangedHandler implements 1813 DisplayChangedListener { 1814 // Empty non private constructor was added because access to this 1815 // class shouldn't be generated by the compiler using synthetic 1816 // accessor method 1817 DisplayChangedHandler() { 1818 } 1819 1820 public void displayChanged() { 1821 scheduleDisplayChanges(); 1822 } 1823 1824 public void paletteChanged() { 1825 } 1826 1827 private static void scheduleDisplayChanges() { 1828 // To avoid threading problems, we notify each RepaintManager 1829 // on the thread it was created on. 1830 for (AppContext context : AppContext.getAppContexts()) { 1831 synchronized(context) { 1832 if (!context.isDisposed()) { 1833 EventQueue eventQueue = (EventQueue)context.get( 1834 AppContext.EVENT_QUEUE_KEY); 1835 if (eventQueue != null) { 1836 eventQueue.postEvent(new InvocationEvent( 1837 Toolkit.getDefaultToolkit(), 1838 new DisplayChangedRunnable())); 1839 } 1840 } 1841 } 1842 } 1843 } 1844 } 1845 1846 1847 private static final class DisplayChangedRunnable implements Runnable { 1848 public void run() { 1849 RepaintManager.currentManager((JComponent)null).displayChanged(); 1850 } 1851 } 1852 1853 1854 /** 1855 * Runnable used to process all repaint/revalidate requests. 1856 */ 1857 private final class ProcessingRunnable implements Runnable { 1858 // If true, we're wainting on the EventQueue. 1859 private boolean pending; 1860 1861 /** 1862 * Marks this processing runnable as pending. If this was not 1863 * already marked as pending, true is returned. 1864 */ 1865 public synchronized boolean markPending() { 1866 if (!pending) { 1867 pending = true; 1868 return true; 1869 } 1870 return false; 1871 } 1872 1873 public void run() { 1874 synchronized (this) { 1875 pending = false; 1876 } 1877 // First pass, flush any heavy paint events into real paint 1878 // events. If there are pending heavy weight requests this will 1879 // result in q'ing this request up one more time. As 1880 // long as no other requests come in between now and the time 1881 // the second one is processed nothing will happen. This is not 1882 // ideal, but the logic needed to suppress the second request is 1883 // more headache than it's worth. 1884 scheduleHeavyWeightPaints(); 1885 // Do the actual validation and painting. 1886 validateInvalidComponents(); 1887 prePaintDirtyRegions(); 1888 } 1889 } 1890 private RepaintManager getDelegate(Component c) { 1891 RepaintManager delegate = SwingUtilities3.getDelegateRepaintManager(c); 1892 if (this == delegate) { 1893 delegate = null; 1894 } 1895 return delegate; 1896 } 1897 } --- EOF ---