RepaintManager.java 26 KB

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