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