Frames | No Frames |
1: /* RepaintManager.java -- 2: Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import java.awt.Component; 42: import java.awt.Dimension; 43: import java.awt.Graphics; 44: import java.awt.Image; 45: import java.awt.Rectangle; 46: import java.awt.Window; 47: import java.awt.image.VolatileImage; 48: import java.util.ArrayList; 49: import java.util.HashMap; 50: import java.util.HashSet; 51: import java.util.Iterator; 52: import java.util.Map; 53: import java.util.Set; 54: import java.util.WeakHashMap; 55: 56: /** 57: * <p>The repaint manager holds a set of dirty regions, invalid components, 58: * and a double buffer surface. The dirty regions and invalid components 59: * are used to coalesce multiple revalidate() and repaint() calls in the 60: * component tree into larger groups to be refreshed "all at once"; the 61: * double buffer surface is used by root components to paint 62: * themselves.</p> 63: * 64: * <p>See <a 65: * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this 66: * document</a> for more details.</p> 67: * 68: * @author Roman Kennke (kennke@aicas.com) 69: * @author Graydon Hoare (graydon@redhat.com) 70: * @author Audrius Meskauskas (audriusa@bioinformatics.org) 71: */ 72: public class RepaintManager 73: { 74: /** 75: * The current repaint managers, indexed by their ThreadGroups. 76: */ 77: static WeakHashMap currentRepaintManagers; 78: 79: /** 80: * A rectangle object to be reused in damaged regions calculation. 81: */ 82: private static Rectangle rectCache = new Rectangle(); 83: 84: /** 85: * <p>A helper class which is placed into the system event queue at 86: * various times in order to facilitate repainting and layout. There is 87: * typically only one of these objects active at any time. When the 88: * {@link RepaintManager} is told to queue a repaint, it checks to see if 89: * a {@link RepaintWorker} is "live" in the system event queue, and if 90: * not it inserts one using {@link SwingUtilities#invokeLater}.</p> 91: * 92: * <p>When the {@link RepaintWorker} comes to the head of the system 93: * event queue, its {@link RepaintWorker#run} method is executed by the 94: * swing paint thread, which revalidates all invalid components and 95: * repaints any damage in the swing scene.</p> 96: */ 97: private class RepaintWorker 98: implements Runnable 99: { 100: 101: boolean live; 102: 103: public RepaintWorker() 104: { 105: live = false; 106: } 107: 108: public synchronized void setLive(boolean b) 109: { 110: live = b; 111: } 112: 113: public synchronized boolean isLive() 114: { 115: return live; 116: } 117: 118: public void run() 119: { 120: try 121: { 122: ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 123: RepaintManager rm = 124: (RepaintManager) currentRepaintManagers.get(threadGroup); 125: rm.validateInvalidComponents(); 126: rm.paintDirtyRegions(); 127: } 128: finally 129: { 130: setLive(false); 131: } 132: } 133: 134: } 135: 136: /** 137: * A table storing the dirty regions of components. The keys of this 138: * table are components, the values are rectangles. Each component maps 139: * to exactly one rectangle. When more regions are marked as dirty on a 140: * component, they are union'ed with the existing rectangle. 141: * 142: * This is package private to avoid a synthetic accessor method in inner 143: * class. 144: * 145: * @see #addDirtyRegion 146: * @see #getDirtyRegion 147: * @see #isCompletelyDirty 148: * @see #markCompletelyClean 149: * @see #markCompletelyDirty 150: */ 151: private HashMap dirtyComponents; 152: 153: /** 154: * The dirtyComponents which is used in paintDiryRegions to avoid unnecessary 155: * locking. 156: */ 157: private HashMap dirtyComponentsWork; 158: 159: /** 160: * A single, shared instance of the helper class. Any methods which mark 161: * components as invalid or dirty eventually activate this instance. It 162: * is added to the event queue if it is not already active, otherwise 163: * reused. 164: * 165: * @see #addDirtyRegion 166: * @see #addInvalidComponent 167: */ 168: private RepaintWorker repaintWorker; 169: 170: /** 171: * The set of components which need revalidation, in the "layout" sense. 172: * There is no additional information about "what kind of layout" they 173: * need (as there is with dirty regions), so it is just a vector rather 174: * than a table. 175: * 176: * @see #addInvalidComponent 177: * @see #removeInvalidComponent 178: * @see #validateInvalidComponents 179: */ 180: private ArrayList invalidComponents; 181: 182: /** 183: * Whether or not double buffering is enabled on this repaint 184: * manager. This is merely a hint to clients; the RepaintManager will 185: * always return an offscreen buffer when one is requested. 186: * 187: * @see #isDoubleBufferingEnabled 188: * @see #setDoubleBufferingEnabled 189: */ 190: private boolean doubleBufferingEnabled; 191: 192: /** 193: * The offscreen buffers. This map holds one offscreen buffer per 194: * Window/Applet and releases them as soon as the Window/Applet gets garbage 195: * collected. 196: */ 197: private WeakHashMap offscreenBuffers; 198: 199: /** 200: * Indicates if the RepaintManager is currently repainting an area. 201: */ 202: private boolean repaintUnderway; 203: 204: /** 205: * This holds buffer commit requests when the RepaintManager is working. 206: * This maps Component objects (the top level components) to Rectangle 207: * objects (the area of the corresponding buffer that must be blitted on 208: * the component). 209: */ 210: private HashMap commitRequests; 211: 212: /** 213: * The maximum width and height to allocate as a double buffer. Requests 214: * beyond this size are ignored. 215: * 216: * @see #paintDirtyRegions 217: * @see #getDoubleBufferMaximumSize 218: * @see #setDoubleBufferMaximumSize 219: */ 220: private Dimension doubleBufferMaximumSize; 221: 222: 223: /** 224: * Create a new RepaintManager object. 225: */ 226: public RepaintManager() 227: { 228: dirtyComponents = new HashMap(); 229: dirtyComponentsWork = new HashMap(); 230: invalidComponents = new ArrayList(); 231: repaintWorker = new RepaintWorker(); 232: doubleBufferMaximumSize = new Dimension(2000,2000); 233: doubleBufferingEnabled = true; 234: offscreenBuffers = new WeakHashMap(); 235: repaintUnderway = false; 236: commitRequests = new HashMap(); 237: } 238: 239: /** 240: * Returns the <code>RepaintManager</code> for the current thread's 241: * thread group. The default implementation ignores the 242: * <code>component</code> parameter and returns the same repaint manager 243: * for all components. 244: * 245: * @param component a component to look up the manager of 246: * 247: * @return the current repaint manager for the calling thread's thread group 248: * and the specified component 249: * 250: * @see #setCurrentManager 251: */ 252: public static RepaintManager currentManager(Component component) 253: { 254: if (currentRepaintManagers == null) 255: currentRepaintManagers = new WeakHashMap(); 256: ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 257: RepaintManager currentManager = 258: (RepaintManager) currentRepaintManagers.get(threadGroup); 259: if (currentManager == null) 260: { 261: currentManager = new RepaintManager(); 262: currentRepaintManagers.put(threadGroup, currentManager); 263: } 264: return currentManager; 265: } 266: 267: /** 268: * Returns the <code>RepaintManager</code> for the current thread's 269: * thread group. The default implementation ignores the 270: * <code>component</code> parameter and returns the same repaint manager 271: * for all components. 272: * 273: * This method is only here for backwards compatibility with older versions 274: * of Swing and simply forwards to {@link #currentManager(Component)}. 275: * 276: * @param component a component to look up the manager of 277: * 278: * @return the current repaint manager for the calling thread's thread group 279: * and the specified component 280: * 281: * @see #setCurrentManager 282: */ 283: public static RepaintManager currentManager(JComponent component) 284: { 285: return currentManager((Component)component); 286: } 287: 288: /** 289: * Sets the repaint manager for the calling thread's thread group. 290: * 291: * @param manager the repaint manager to set for the current thread's thread 292: * group 293: * 294: * @see #currentManager(Component) 295: */ 296: public static void setCurrentManager(RepaintManager manager) 297: { 298: if (currentRepaintManagers == null) 299: currentRepaintManagers = new WeakHashMap(); 300: 301: ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); 302: currentRepaintManagers.put(threadGroup, manager); 303: } 304: 305: /** 306: * Add a component to the {@link #invalidComponents} vector. If the 307: * {@link #repaintWorker} class is not active, insert it in the system 308: * event queue. 309: * 310: * @param component The component to add 311: * 312: * @see #removeInvalidComponent 313: */ 314: public void addInvalidComponent(JComponent component) 315: { 316: Component validateRoot = null; 317: Component c = component; 318: while (c != null) 319: { 320: // Special cases we don't bother validating are when the invalidated 321: // component (or any of it's ancestors) is inside a CellRendererPane 322: // or if it doesn't have a peer yet (== not displayable). 323: if (c instanceof CellRendererPane || ! c.isDisplayable()) 324: return; 325: if (c instanceof JComponent && ((JComponent) c).isValidateRoot()) 326: { 327: validateRoot = c; 328: break; 329: } 330: 331: c = c.getParent(); 332: } 333: 334: // If we didn't find a validate root, then we don't validate. 335: if (validateRoot == null) 336: return; 337: 338: // Make sure the validate root and all of it's ancestors are visible. 339: c = validateRoot; 340: while (c != null) 341: { 342: if (! c.isVisible() || ! c.isDisplayable()) 343: return; 344: c = c.getParent(); 345: } 346: 347: if (invalidComponents.contains(validateRoot)) 348: return; 349: 350: //synchronized (invalidComponents) 351: // { 352: invalidComponents.add(validateRoot); 353: // } 354: 355: if (! repaintWorker.isLive()) 356: { 357: repaintWorker.setLive(true); 358: SwingUtilities.invokeLater(repaintWorker); 359: } 360: } 361: 362: /** 363: * Remove a component from the {@link #invalidComponents} vector. 364: * 365: * @param component The component to remove 366: * 367: * @see #addInvalidComponent 368: */ 369: public void removeInvalidComponent(JComponent component) 370: { 371: synchronized (invalidComponents) 372: { 373: invalidComponents.remove(component); 374: } 375: } 376: 377: /** 378: * Add a region to the set of dirty regions for a specified component. 379: * This involves union'ing the new region with any existing dirty region 380: * associated with the component. If the {@link #repaintWorker} class 381: * is not active, insert it in the system event queue. 382: * 383: * @param component The component to add a dirty region for 384: * @param x The left x coordinate of the new dirty region 385: * @param y The top y coordinate of the new dirty region 386: * @param w The width of the new dirty region 387: * @param h The height of the new dirty region 388: * 389: * @see #addDirtyRegion 390: * @see #getDirtyRegion 391: * @see #isCompletelyDirty 392: * @see #markCompletelyClean 393: * @see #markCompletelyDirty 394: */ 395: public void addDirtyRegion(JComponent component, int x, int y, 396: int w, int h) 397: { 398: if (w <= 0 || h <= 0 || !component.isShowing()) 399: return; 400: 401: Component parent = component.getParent(); 402: 403: component.computeVisibleRect(rectCache); 404: SwingUtilities.computeIntersection(x, y, w, h, rectCache); 405: 406: if (! rectCache.isEmpty()) 407: { 408: if (dirtyComponents.containsKey(component)) 409: { 410: SwingUtilities.computeUnion(rectCache.x, rectCache.y, 411: rectCache.width, rectCache.height, 412: (Rectangle) dirtyComponents.get(component)); 413: } 414: else 415: { 416: synchronized (dirtyComponents) 417: { 418: dirtyComponents.put(component, rectCache.getBounds()); 419: } 420: } 421: 422: if (! repaintWorker.isLive()) 423: { 424: repaintWorker.setLive(true); 425: SwingUtilities.invokeLater(repaintWorker); 426: } 427: } 428: } 429: 430: /** 431: * Get the dirty region associated with a component, or <code>null</code> 432: * if the component has no dirty region. 433: * 434: * @param component The component to get the dirty region of 435: * 436: * @return The dirty region of the component 437: * 438: * @see #dirtyComponents 439: * @see #addDirtyRegion 440: * @see #isCompletelyDirty 441: * @see #markCompletelyClean 442: * @see #markCompletelyDirty 443: */ 444: public Rectangle getDirtyRegion(JComponent component) 445: { 446: Rectangle dirty = (Rectangle) dirtyComponents.get(component); 447: if (dirty == null) 448: dirty = new Rectangle(); 449: return dirty; 450: } 451: 452: /** 453: * Mark a component as dirty over its entire bounds. 454: * 455: * @param component The component to mark as dirty 456: * 457: * @see #dirtyComponents 458: * @see #addDirtyRegion 459: * @see #getDirtyRegion 460: * @see #isCompletelyDirty 461: * @see #markCompletelyClean 462: */ 463: public void markCompletelyDirty(JComponent component) 464: { 465: addDirtyRegion(component, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); 466: } 467: 468: /** 469: * Remove all dirty regions for a specified component 470: * 471: * @param component The component to mark as clean 472: * 473: * @see #dirtyComponents 474: * @see #addDirtyRegion 475: * @see #getDirtyRegion 476: * @see #isCompletelyDirty 477: * @see #markCompletelyDirty 478: */ 479: public void markCompletelyClean(JComponent component) 480: { 481: synchronized (dirtyComponents) 482: { 483: dirtyComponents.remove(component); 484: } 485: } 486: 487: /** 488: * Return <code>true</code> if the specified component is completely 489: * contained within its dirty region, otherwise <code>false</code> 490: * 491: * @param component The component to check for complete dirtyness 492: * 493: * @return Whether the component is completely dirty 494: * 495: * @see #dirtyComponents 496: * @see #addDirtyRegion 497: * @see #getDirtyRegion 498: * @see #isCompletelyDirty 499: * @see #markCompletelyClean 500: */ 501: public boolean isCompletelyDirty(JComponent component) 502: { 503: boolean dirty = false; 504: Rectangle r = getDirtyRegion(component); 505: if(r.width == Integer.MAX_VALUE && r.height == Integer.MAX_VALUE) 506: dirty = true; 507: return dirty; 508: } 509: 510: /** 511: * Validate all components which have been marked invalid in the {@link 512: * #invalidComponents} vector. 513: */ 514: public void validateInvalidComponents() 515: { 516: // We don't use an iterator here because that would fail when there are 517: // components invalidated during the validation of others, which happens 518: // quite frequently. Instead we synchronize the access a little more. 519: while (invalidComponents.size() > 0) 520: { 521: Component comp; 522: synchronized (invalidComponents) 523: { 524: comp = (Component) invalidComponents.remove(0); 525: } 526: // Validate the validate component. 527: if (! (comp.isVisible() && comp.isShowing())) 528: continue; 529: comp.validate(); 530: } 531: } 532: 533: /** 534: * Repaint all regions of all components which have been marked dirty in the 535: * {@link #dirtyComponents} table. 536: */ 537: public void paintDirtyRegions() 538: { 539: // Short cicuit if there is nothing to paint. 540: if (dirtyComponents.size() == 0) 541: return; 542: 543: // Swap dirtyRegions with dirtyRegionsWork to avoid locking. 544: synchronized (dirtyComponents) 545: { 546: HashMap swap = dirtyComponents; 547: dirtyComponents = dirtyComponentsWork; 548: dirtyComponentsWork = swap; 549: } 550: 551: // Compile a set of repaint roots. 552: HashSet repaintRoots = new HashSet(); 553: Set components = dirtyComponentsWork.keySet(); 554: for (Iterator i = components.iterator(); i.hasNext();) 555: { 556: JComponent dirty = (JComponent) i.next(); 557: compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots); 558: } 559: 560: repaintUnderway = true; 561: for (Iterator i = repaintRoots.iterator(); i.hasNext();) 562: { 563: JComponent comp = (JComponent) i.next(); 564: Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp); 565: if (damaged == null || damaged.isEmpty()) 566: continue; 567: comp.paintImmediately(damaged); 568: } 569: dirtyComponentsWork.clear(); 570: repaintUnderway = false; 571: commitRemainingBuffers(); 572: } 573: 574: /** 575: * Compiles a list of components that really get repainted. This is called 576: * once for each component in the dirtyComponents HashMap, each time with 577: * another <code>dirty</code> parameter. This searches up the component 578: * hierarchy of <code>dirty</code> to find the highest parent that is also 579: * marked dirty and merges the dirty regions. 580: * 581: * @param dirtyRegions the dirty regions 582: * @param dirty the component for which to find the repaint root 583: * @param roots the list to which new repaint roots get appended 584: */ 585: private void compileRepaintRoots(HashMap dirtyRegions, JComponent dirty, 586: HashSet roots) 587: { 588: Component current = dirty; 589: Component root = dirty; 590: 591: // Search the highest component that is also marked dirty. 592: Component parent; 593: while (true) 594: { 595: parent = current.getParent(); 596: if (parent == null || !(parent instanceof JComponent)) 597: break; 598: 599: current = parent; 600: // We can skip to the next up when this parent is not dirty. 601: if (dirtyRegions.containsKey(parent)) 602: { 603: root = current; 604: } 605: } 606: 607: // Merge the rectangles of the root and the requested component if 608: // the are different. 609: if (root != dirty) 610: { 611: Rectangle dirtyRect = (Rectangle) dirtyRegions.get(dirty); 612: dirtyRect = SwingUtilities.convertRectangle(dirty, dirtyRect, root); 613: Rectangle rootRect = (Rectangle) dirtyRegions.get(root); 614: SwingUtilities.computeUnion(dirtyRect.x, dirtyRect.y, dirtyRect.width, 615: dirtyRect.height, rootRect); 616: } 617: 618: // Adds the root to the roots set. 619: roots.add(root); 620: } 621: 622: /** 623: * Get an offscreen buffer for painting a component's image. This image 624: * may be smaller than the proposed dimensions, depending on the value of 625: * the {@link #doubleBufferMaximumSize} property. 626: * 627: * @param component The component to return an offscreen buffer for 628: * @param proposedWidth The proposed width of the offscreen buffer 629: * @param proposedHeight The proposed height of the offscreen buffer 630: * 631: * @return A shared offscreen buffer for painting 632: */ 633: public Image getOffscreenBuffer(Component component, int proposedWidth, 634: int proposedHeight) 635: { 636: Component root = SwingUtilities.getWindowAncestor(component); 637: Image buffer = (Image) offscreenBuffers.get(root); 638: if (buffer == null 639: || buffer.getWidth(null) < proposedWidth 640: || buffer.getHeight(null) < proposedHeight) 641: { 642: int width = Math.max(proposedWidth, root.getWidth()); 643: width = Math.min(doubleBufferMaximumSize.width, width); 644: int height = Math.max(proposedHeight, root.getHeight()); 645: height = Math.min(doubleBufferMaximumSize.height, height); 646: buffer = root.createImage(width, height); 647: offscreenBuffers.put(root, buffer); 648: } 649: return buffer; 650: } 651: 652: /** 653: * Blits the back buffer of the specified root component to the screen. If 654: * the RepaintManager is currently working on a paint request, the commit 655: * requests are queued up and committed at once when the paint request is 656: * done (by {@link #commitRemainingBuffers}). This is package private because 657: * it must get called by JComponent. 658: * 659: * @param comp the component to be painted 660: * @param area the area to paint on screen, in comp coordinates 661: */ 662: void commitBuffer(Component comp, Rectangle area) 663: { 664: // Determine the component that we finally paint the buffer upon. 665: // We need to paint on the nearest heavyweight component, so that Swing 666: // hierarchies inside (non-window) heavyweights get painted correctly. 667: // Otherwise we would end up blitting the backbuffer behind the heavyweight 668: // which is wrong. 669: Component root = getHeavyweightParent(comp); 670: // FIXME: Optimize this. 671: Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root); 672: 673: // We synchronize on dirtyComponents here because that is what 674: // paintDirtyRegions also synchronizes on while painting. 675: synchronized (dirtyComponents) 676: { 677: // If the RepaintManager is not currently painting, then directly 678: // blit the requested buffer on the screen. 679: if (true || ! repaintUnderway) 680: { 681: blitBuffer(root, rootRect); 682: } 683: 684: // Otherwise queue this request up, until all the RepaintManager work 685: // is done. 686: else 687: { 688: if (commitRequests.containsKey(root)) 689: SwingUtilities.computeUnion(rootRect.x, rootRect.y, 690: rootRect.width, rootRect.height, 691: (Rectangle) commitRequests.get(root)); 692: else 693: commitRequests.put(root, rootRect); 694: } 695: } 696: } 697: 698: /** 699: * Copies the buffer to the screen. Note that the root component here is 700: * not necessarily the component with which the offscreen buffer is 701: * associated. The offscreen buffers are always allocated for the toplevel 702: * windows. However, painted is performed on lower-level heavyweight 703: * components too, if they contain Swing components. 704: * 705: * @param root the heavyweight component to blit upon 706: * @param rootRect the rectangle in the root component's coordinate space 707: */ 708: private void blitBuffer(Component root, Rectangle rootRect) 709: { 710: if (! root.isShowing()) 711: return; 712: 713: // Find the Window from which we use the backbuffer. 714: Component bufferRoot = root; 715: Rectangle bufferRect = rootRect.getBounds(); 716: if (!(bufferRoot instanceof Window)) 717: { 718: bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot); 719: SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot); 720: } 721: 722: Graphics g = root.getGraphics(); 723: Image buffer = (Image) offscreenBuffers.get(bufferRoot); 724: 725: // Make sure we have a sane clip at this point. 726: g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height); 727: g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y, 728: root); 729: g.dispose(); 730: 731: } 732: 733: /** 734: * Finds and returns the nearest heavyweight parent for the specified 735: * component. If the component isn't contained inside a heavyweight parent, 736: * this returns null. 737: * 738: * @param comp the component 739: * 740: * @return the nearest heavyweight parent for the specified component or 741: * null if the component has no heavyweight ancestor 742: */ 743: private Component getHeavyweightParent(Component comp) 744: { 745: while (comp != null && comp.isLightweight()) 746: comp = comp.getParent(); 747: return comp; 748: } 749: 750: /** 751: * Commits the queued up back buffers to screen all at once. 752: */ 753: private void commitRemainingBuffers() 754: { 755: // We synchronize on dirtyComponents here because that is what 756: // paintDirtyRegions also synchronizes on while painting. 757: synchronized (dirtyComponents) 758: { 759: Set entrySet = commitRequests.entrySet(); 760: Iterator i = entrySet.iterator(); 761: while (i.hasNext()) 762: { 763: Map.Entry entry = (Map.Entry) i.next(); 764: Component root = (Component) entry.getKey(); 765: Rectangle area = (Rectangle) entry.getValue(); 766: blitBuffer(root, area); 767: i.remove(); 768: } 769: } 770: } 771: 772: /** 773: * Creates and returns a volatile offscreen buffer for the specified 774: * component that can be used as a double buffer. The returned image 775: * is a {@link VolatileImage}. Its size will be <code>(proposedWidth, 776: * proposedHeight)</code> except when the maximum double buffer size 777: * has been set in this RepaintManager. 778: * 779: * @param comp the Component for which to create a volatile buffer 780: * @param proposedWidth the proposed width of the buffer 781: * @param proposedHeight the proposed height of the buffer 782: * 783: * @since 1.4 784: * 785: * @see VolatileImage 786: */ 787: public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth, 788: int proposedHeight) 789: { 790: Component root = SwingUtilities.getWindowAncestor(comp); 791: Image buffer = (Image) offscreenBuffers.get(root); 792: if (buffer == null 793: || buffer.getWidth(null) < proposedWidth 794: || buffer.getHeight(null) < proposedHeight 795: || !(buffer instanceof VolatileImage)) 796: { 797: int width = Math.max(proposedWidth, root.getWidth()); 798: width = Math.min(doubleBufferMaximumSize.width, width); 799: int height = Math.max(proposedHeight, root.getHeight()); 800: height = Math.min(doubleBufferMaximumSize.height, height); 801: buffer = root.createVolatileImage(width, height); 802: if (buffer != null) 803: offscreenBuffers.put(root, buffer); 804: } 805: return buffer; 806: } 807: 808: 809: /** 810: * Get the value of the {@link #doubleBufferMaximumSize} property. 811: * 812: * @return The current value of the property 813: * 814: * @see #setDoubleBufferMaximumSize 815: */ 816: public Dimension getDoubleBufferMaximumSize() 817: { 818: return doubleBufferMaximumSize; 819: } 820: 821: /** 822: * Set the value of the {@link #doubleBufferMaximumSize} property. 823: * 824: * @param size The new value of the property 825: * 826: * @see #getDoubleBufferMaximumSize 827: */ 828: public void setDoubleBufferMaximumSize(Dimension size) 829: { 830: doubleBufferMaximumSize = size; 831: } 832: 833: /** 834: * Set the value of the {@link #doubleBufferingEnabled} property. 835: * 836: * @param buffer The new value of the property 837: * 838: * @see #isDoubleBufferingEnabled 839: */ 840: public void setDoubleBufferingEnabled(boolean buffer) 841: { 842: doubleBufferingEnabled = buffer; 843: } 844: 845: /** 846: * Get the value of the {@link #doubleBufferingEnabled} property. 847: * 848: * @return The current value of the property 849: * 850: * @see #setDoubleBufferingEnabled 851: */ 852: public boolean isDoubleBufferingEnabled() 853: { 854: return doubleBufferingEnabled; 855: } 856: 857: public String toString() 858: { 859: return "RepaintManager"; 860: } 861: }