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