1. /*
  2. * @(#)RepaintManager.java 1.60 04/02/18
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.awt.peer.ComponentPeer;
  11. import java.awt.peer.ContainerPeer;
  12. import java.awt.image.VolatileImage;
  13. import java.util.*;
  14. import java.applet.*;
  15. import sun.security.action.GetPropertyAction;
  16. /**
  17. * This class manages repaint requests, allowing the number
  18. * of repaints to be minimized, for example by collapsing multiple
  19. * requests into a single repaint for members of a component tree.
  20. *
  21. * @version 1.60 02/18/04
  22. * @author Arnaud Weber
  23. */
  24. public class RepaintManager
  25. {
  26. /**
  27. * Maps from GraphicsConfiguration to VolatileImage.
  28. */
  29. private Map volatileMap = new HashMap(1);
  30. Hashtable dirtyComponents = new Hashtable();
  31. Hashtable tmpDirtyComponents = new Hashtable();
  32. Vector invalidComponents;
  33. boolean doubleBufferingEnabled = true;
  34. private Dimension doubleBufferMaxSize;
  35. // Support for both the standard and volatile offscreen buffers exists to
  36. // provide backwards compatibility for the [rare] programs which may be
  37. // calling getOffScreenBuffer() and not expecting to get a VolatileImage.
  38. // Swing internally is migrating to use *only* the volatile image buffer.
  39. // Support for standard offscreen buffer
  40. //
  41. DoubleBufferInfo standardDoubleBuffer;
  42. private static final Object repaintManagerKey = RepaintManager.class;
  43. // Whether or not a VolatileImage should be used for double-buffered painting
  44. static boolean volatileImageBufferEnabled = true;
  45. // The maximum number of times Swing will attempt to use the VolatileImage
  46. // buffer during a paint operation.
  47. static final int VOLATILE_LOOP_MAX = 2;
  48. static {
  49. String vib = (String) java.security.AccessController.doPrivileged(
  50. new GetPropertyAction("swing.volatileImageBufferEnabled"));
  51. volatileImageBufferEnabled = (vib == null || vib.equals("true"));
  52. }
  53. /**
  54. * Return the RepaintManager for the calling thread given a Component.
  55. *
  56. * @param c a Component -- unused in the default implementation, but could
  57. * be used by an overridden version to return a different RepaintManager
  58. * depending on the Component
  59. * @return the RepaintManager object
  60. */
  61. public static RepaintManager currentManager(Component c) {
  62. // Note: SystemEventQueueUtilities.ComponentWorkRequest passes
  63. // in null as the component, so if component is ever used to
  64. // determine the current RepaintManager, SystemEventQueueUtilities
  65. // will need to be modified accordingly.
  66. RepaintManager result = (RepaintManager) SwingUtilities.appContextGet(repaintManagerKey);
  67. if(result == null) {
  68. result = new RepaintManager();
  69. SwingUtilities.appContextPut(repaintManagerKey, result);
  70. }
  71. return result;
  72. }
  73. /**
  74. * Return the RepaintManager for the calling thread given a JComponent.
  75. * <p>
  76. * Note: This method exists for backward binary compatibility with earlier
  77. * versions of the Swing library. It simply returns the result returned by
  78. * {@link #currentManager(Component)}.
  79. *
  80. * @param c a JComponent -- unused
  81. * @return the RepaintManager object
  82. */
  83. public static RepaintManager currentManager(JComponent c) {
  84. return currentManager((Component)c);
  85. }
  86. /**
  87. * Set the RepaintManager that should be used for the calling
  88. * thread. <b>aRepaintManager</b> will become the current RepaintManager
  89. * for the calling thread's thread group.
  90. * @param aRepaintManager the RepaintManager object to use
  91. */
  92. public static void setCurrentManager(RepaintManager aRepaintManager) {
  93. if (aRepaintManager != null) {
  94. SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
  95. } else {
  96. SwingUtilities.appContextRemove(repaintManagerKey);
  97. }
  98. }
  99. /**
  100. * Create a new RepaintManager instance. You rarely call this constructor.
  101. * directly. To get the default RepaintManager, use
  102. * RepaintManager.currentManager(JComponent) (normally "this").
  103. */
  104. public RepaintManager() {
  105. Object dbe = java.security.AccessController.doPrivileged(
  106. new GetPropertyAction("awt.nativeDoubleBuffering"));
  107. boolean nativeDoubleBuffering = (dbe != null) ?
  108. Boolean.valueOf(dbe.toString()).booleanValue() : false;
  109. // If native doublebuffering is being used, do NOT use
  110. // Swing doublebuffering.
  111. doubleBufferingEnabled = !nativeDoubleBuffering;
  112. }
  113. /**
  114. * Mark the component as in need of layout and queue a runnable
  115. * for the event dispatching thread that will validate the components
  116. * first isValidateRoot() ancestor.
  117. *
  118. * @see JComponent#isValidateRoot
  119. * @see #removeInvalidComponent
  120. */
  121. public synchronized void addInvalidComponent(JComponent invalidComponent)
  122. {
  123. Component validateRoot = null;
  124. /* Find the first JComponent ancestor of this component whose
  125. * isValidateRoot() method returns true.
  126. */
  127. for(Component c = invalidComponent; c != null; c = c.getParent()) {
  128. if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
  129. return;
  130. }
  131. if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {
  132. validateRoot = c;
  133. break;
  134. }
  135. }
  136. /* There's no validateRoot to apply validate to, so we're done.
  137. */
  138. if (validateRoot == null) {
  139. return;
  140. }
  141. /* If the validateRoot and all of its ancestors aren't visible
  142. * then we don't do anything. While we're walking up the tree
  143. * we find the root Window or Applet.
  144. */
  145. Component root = null;
  146. for(Component c = validateRoot; c != null; c = c.getParent()) {
  147. if (!c.isVisible() || (c.getPeer() == null)) {
  148. return;
  149. }
  150. if ((c instanceof Window) || (c instanceof Applet)) {
  151. root = c;
  152. break;
  153. }
  154. }
  155. if (root == null) {
  156. return;
  157. }
  158. /* Lazily create the invalidateComponents vector and add the
  159. * validateRoot if it's not there already. If this validateRoot
  160. * is already in the vector, we're done.
  161. */
  162. if (invalidComponents == null) {
  163. invalidComponents = new Vector();
  164. }
  165. else {
  166. int n = invalidComponents.size();
  167. for(int i = 0; i < n; i++) {
  168. if(validateRoot == (Component)(invalidComponents.elementAt(i))) {
  169. return;
  170. }
  171. }
  172. }
  173. invalidComponents.addElement(validateRoot);
  174. /* Queues a Runnable that calls RepaintManager.validateInvalidComponents()
  175. * and RepaintManager.paintDirtyRegions() with SwingUtilities.invokeLater().
  176. */
  177. SystemEventQueueUtilities.queueComponentWorkRequest(root);
  178. }
  179. /**
  180. * Remove a component from the list of invalid components.
  181. *
  182. * @see #addInvalidComponent
  183. */
  184. public synchronized void removeInvalidComponent(JComponent component) {
  185. if(invalidComponents != null) {
  186. int index = invalidComponents.indexOf(component);
  187. if(index != -1) {
  188. invalidComponents.removeElementAt(index);
  189. }
  190. }
  191. }
  192. /**
  193. * Add a component in the list of components that should be refreshed.
  194. * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
  195. * will be unioned with the region that should be redrawn.
  196. *
  197. * @see JComponent#repaint
  198. */
  199. public void addDirtyRegion(JComponent c, int x, int y, int w, int h)
  200. {
  201. /* Special cases we don't have to bother with.
  202. */
  203. if ((w <= 0) || (h <= 0) || (c == null)) {
  204. return;
  205. }
  206. if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
  207. return;
  208. }
  209. if (extendDirtyRegion(c, x, y, w, h)) {
  210. // Component was already marked as dirty, region has been
  211. // extended, no need to continue.
  212. return;
  213. }
  214. /* Make sure that c and all it ancestors (up to an Applet or
  215. * Window) are visible. This loop has the same effect as
  216. * checking c.isShowing() (and note that it's still possible
  217. * that c is completely obscured by an opaque ancestor in
  218. * the specified rectangle).
  219. */
  220. Component root = null;
  221. // Note: We can't synchronize around this, Frame.getExtendedState
  222. // is synchronized so that if we were to synchronize around this
  223. // it could lead to the possibility of getting locks out
  224. // of order and deadlocking.
  225. for (Container p = c; p != null; p = p.getParent()) {
  226. if (!p.isVisible() || (p.getPeer() == null)) {
  227. return;
  228. }
  229. if ((p instanceof Window) || (p instanceof Applet)) {
  230. // Iconified frames are still visible!
  231. if (p instanceof Frame &&
  232. (((Frame)p).getExtendedState() & Frame.ICONIFIED) ==
  233. Frame.ICONIFIED) {
  234. return;
  235. }
  236. root = p;
  237. break;
  238. }
  239. }
  240. if (root == null) return;
  241. synchronized(this) {
  242. if (extendDirtyRegion(c, x, y, w, h)) {
  243. // In between last check and this check another thread
  244. // queued up runnable, can bail here.
  245. return;
  246. }
  247. dirtyComponents.put(c, new Rectangle(x, y, w, h));
  248. }
  249. /* Queues a Runnable that calls validateInvalidComponents() and
  250. * rm.paintDirtyRegions() with SwingUtilities.invokeLater().
  251. */
  252. SystemEventQueueUtilities.queueComponentWorkRequest(root);
  253. }
  254. /**
  255. * Extends the dirty region for the specified component to include
  256. * the new region.
  257. *
  258. * @return false if <code>c</code> is not yet marked dirty.
  259. */
  260. private synchronized boolean extendDirtyRegion(
  261. Component c, int x, int y, int w, int h) {
  262. Rectangle r = (Rectangle)dirtyComponents.get(c);
  263. if (r != null) {
  264. // A non-null r implies c is already marked as dirty,
  265. // and that the parent is valid. Therefore we can
  266. // just union the rect and bail.
  267. SwingUtilities.computeUnion(x, y, w, h, r);
  268. return true;
  269. }
  270. return false;
  271. }
  272. /** Return the current dirty region for a component.
  273. * Return an empty rectangle if the component is not
  274. * dirty.
  275. */
  276. public Rectangle getDirtyRegion(JComponent aComponent) {
  277. Rectangle r = null;
  278. synchronized(this) {
  279. r = (Rectangle)dirtyComponents.get(aComponent);
  280. }
  281. if(r == null)
  282. return new Rectangle(0,0,0,0);
  283. else
  284. return new Rectangle(r);
  285. }
  286. /**
  287. * Mark a component completely dirty. <b>aComponent</b> will be
  288. * completely painted during the next paintDirtyRegions() call.
  289. */
  290. public void markCompletelyDirty(JComponent aComponent) {
  291. addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
  292. }
  293. /**
  294. * Mark a component completely clean. <b>aComponent</b> will not
  295. * get painted during the next paintDirtyRegions() call.
  296. */
  297. public void markCompletelyClean(JComponent aComponent) {
  298. synchronized(this) {
  299. dirtyComponents.remove(aComponent);
  300. }
  301. }
  302. /**
  303. * Convenience method that returns true if <b>aComponent</b> will be completely
  304. * painted during the next paintDirtyRegions(). If computing dirty regions is
  305. * expensive for your component, use this method and avoid computing dirty region
  306. * if it return true.
  307. */
  308. public boolean isCompletelyDirty(JComponent aComponent) {
  309. Rectangle r;
  310. r = getDirtyRegion(aComponent);
  311. if(r.width == Integer.MAX_VALUE &&
  312. r.height == Integer.MAX_VALUE)
  313. return true;
  314. else
  315. return false;
  316. }
  317. /**
  318. * Validate all of the components that have been marked invalid.
  319. * @see #addInvalidComponent
  320. */
  321. public void validateInvalidComponents() {
  322. Vector ic;
  323. synchronized(this) {
  324. if(invalidComponents == null) {
  325. return;
  326. }
  327. ic = invalidComponents;
  328. invalidComponents = null;
  329. }
  330. int n = ic.size();
  331. for(int i = 0; i < n; i++) {
  332. ((Component)ic.elementAt(i)).validate();
  333. }
  334. }
  335. /**
  336. * Paint all of the components that have been marked dirty.
  337. *
  338. * @see #addDirtyRegion
  339. */
  340. public void paintDirtyRegions() {
  341. int i, count;
  342. Vector roots;
  343. JComponent dirtyComponent;
  344. synchronized(this) { // swap for thread safety
  345. Hashtable tmp = tmpDirtyComponents;
  346. tmpDirtyComponents = dirtyComponents;
  347. dirtyComponents = tmp;
  348. dirtyComponents.clear();
  349. }
  350. count = tmpDirtyComponents.size();
  351. if (count == 0) {
  352. return;
  353. }
  354. Rectangle rect;
  355. int localBoundsX = 0;
  356. int localBoundsY = 0;
  357. int localBoundsH = 0;
  358. int localBoundsW = 0;
  359. Enumeration keys;
  360. roots = new Vector(count);
  361. keys = tmpDirtyComponents.keys();
  362. while(keys.hasMoreElements()) {
  363. dirtyComponent = (JComponent) keys.nextElement();
  364. collectDirtyComponents(tmpDirtyComponents, dirtyComponent, roots);
  365. }
  366. count = roots.size();
  367. // System.out.println("roots size is " + count);
  368. for(i=0 ; i < count ; i++) {
  369. dirtyComponent = (JComponent) roots.elementAt(i);
  370. rect = (Rectangle) tmpDirtyComponents.get(dirtyComponent);
  371. // System.out.println("Should refresh :" + rect);
  372. localBoundsH = dirtyComponent.getHeight();
  373. localBoundsW = dirtyComponent.getWidth();
  374. SwingUtilities.computeIntersection(localBoundsX,
  375. localBoundsY,
  376. localBoundsW,
  377. localBoundsH,
  378. rect);
  379. // System.out.println("** paint of " + dirtyComponent + rect);
  380. if (rect.x == 0 && rect.y == 0 &&
  381. rect.width == dirtyComponent.getWidth() &&
  382. rect.height == dirtyComponent.getHeight()) {
  383. Container parent = dirtyComponent.getParent();
  384. ComponentPeer parentPeer;
  385. if (parent != null && !parent.isLightweight() &&
  386. (parentPeer = parent.getPeer()) != null) {
  387. // Cancel any pending paints on the heavy weight peer.
  388. // This avoid duplicate painting.
  389. ((ContainerPeer)parentPeer).cancelPendingPaint(
  390. dirtyComponent.getX(),
  391. dirtyComponent.getY(),
  392. rect.width, rect.height);
  393. }
  394. }
  395. dirtyComponent.paintImmediately(rect.x,rect.y,rect.width,rect.height);
  396. }
  397. tmpDirtyComponents.clear();
  398. }
  399. Rectangle tmp = new Rectangle();
  400. void collectDirtyComponents(Hashtable dirtyComponents,
  401. JComponent dirtyComponent,
  402. Vector roots) {
  403. int dx, dy, rootDx, rootDy;
  404. Component component, rootDirtyComponent,parent;
  405. //Rectangle tmp;
  406. Rectangle cBounds;
  407. // Find the highest parent which is dirty. When we get out of this
  408. // rootDx and rootDy will contain the translation from the
  409. // rootDirtyComponent's coordinate system to the coordinates of the
  410. // original dirty component. The tmp Rect is also used to compute the
  411. // visible portion of the dirtyRect.
  412. component = rootDirtyComponent = dirtyComponent;
  413. int x = dirtyComponent.getX();
  414. int y = dirtyComponent.getY();
  415. int w = dirtyComponent.getWidth();
  416. int h = dirtyComponent.getHeight();
  417. dx = rootDx = 0;
  418. dy = rootDy = 0;
  419. tmp.setBounds((Rectangle) dirtyComponents.get(dirtyComponent));
  420. // System.out.println("Collect dirty component for bound " + tmp +
  421. // "component bounds is " + cBounds);;
  422. SwingUtilities.computeIntersection(0,0,w,h,tmp);
  423. if (tmp.isEmpty()) {
  424. // System.out.println("Empty 1");
  425. return;
  426. }
  427. for(;;) {
  428. parent = component.getParent();
  429. if(parent == null)
  430. break;
  431. if(!(parent instanceof JComponent))
  432. break;
  433. component = parent;
  434. dx += x;
  435. dy += y;
  436. tmp.setLocation(tmp.x + x, tmp.y + y);
  437. x = component.getX();
  438. y = component.getY();
  439. w = component.getWidth();
  440. h = component.getHeight();
  441. tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp);
  442. if (tmp.isEmpty()) {
  443. // System.out.println("Empty 2");
  444. return;
  445. }
  446. if (dirtyComponents.get(component) != null) {
  447. rootDirtyComponent = component;
  448. rootDx = dx;
  449. rootDy = dy;
  450. }
  451. }
  452. if (dirtyComponent != rootDirtyComponent) {
  453. Rectangle r;
  454. tmp.setLocation(tmp.x + rootDx - dx,
  455. tmp.y + rootDy - dy);
  456. r = (Rectangle)dirtyComponents.get(rootDirtyComponent);
  457. SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
  458. }
  459. // If we haven't seen this root before, then we need to add it to the
  460. // list of root dirty Views.
  461. if (!roots.contains(rootDirtyComponent))
  462. roots.addElement(rootDirtyComponent);
  463. }
  464. /**
  465. * Returns a string that displays and identifies this
  466. * object's properties.
  467. *
  468. * @return a String representation of this object
  469. */
  470. public synchronized String toString() {
  471. StringBuffer sb = new StringBuffer();
  472. if(dirtyComponents != null)
  473. sb.append("" + dirtyComponents);
  474. return sb.toString();
  475. }
  476. /**
  477. * Return the offscreen buffer that should be used as a double buffer with
  478. * the component <code>c</code>.
  479. * By default there is a double buffer per RepaintManager.
  480. * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
  481. * This happens when the maximum double buffer size as been set for the receiving
  482. * repaint manager.
  483. */
  484. public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
  485. return _getOffscreenBuffer(c, proposedWidth, proposedHeight);
  486. }
  487. /**
  488. * Return a volatile offscreen buffer that should be used as a
  489. * double buffer with the specified component <code>c</code>.
  490. * The image returned will be an instance of VolatileImage, or null
  491. * if a VolatileImage object could not be instantiated.
  492. * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>.
  493. * This happens when the maximum double buffer size has been set for this
  494. * repaint manager.
  495. *
  496. * @see java.awt.image.VolatileImage
  497. * @since 1.4
  498. */
  499. public Image getVolatileOffscreenBuffer(Component c,
  500. int proposedWidth,int proposedHeight) {
  501. GraphicsConfiguration config = c.getGraphicsConfiguration();
  502. if (config == null) {
  503. config = GraphicsEnvironment.getLocalGraphicsEnvironment().
  504. getDefaultScreenDevice().getDefaultConfiguration();
  505. }
  506. Dimension maxSize = getDoubleBufferMaximumSize();
  507. int width = proposedWidth < 1 ? 1 :
  508. (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
  509. int height = proposedHeight < 1 ? 1 :
  510. (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
  511. VolatileImage image = (VolatileImage)volatileMap.get(config);
  512. if (image == null || image.getWidth() < width ||
  513. image.getHeight() < height) {
  514. if (image != null) {
  515. image.flush();
  516. }
  517. image = config.createCompatibleVolatileImage(width, height);
  518. volatileMap.put(config, image);
  519. }
  520. return image;
  521. }
  522. private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
  523. Dimension maxSize = getDoubleBufferMaximumSize();
  524. DoubleBufferInfo doubleBuffer = null;
  525. int width, height;
  526. if (standardDoubleBuffer == null) {
  527. standardDoubleBuffer = new DoubleBufferInfo();
  528. }
  529. doubleBuffer = standardDoubleBuffer;
  530. width = proposedWidth < 1? 1 :
  531. (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
  532. height = proposedHeight < 1? 1 :
  533. (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
  534. if (doubleBuffer.needsReset || (doubleBuffer.image != null &&
  535. (doubleBuffer.size.width < width ||
  536. doubleBuffer.size.height < height))) {
  537. doubleBuffer.needsReset = false;
  538. if (doubleBuffer.image != null) {
  539. doubleBuffer.image.flush();
  540. doubleBuffer.image = null;
  541. }
  542. width = Math.max(doubleBuffer.size.width, width);
  543. height = Math.max(doubleBuffer.size.height, height);
  544. }
  545. Image result = doubleBuffer.image;
  546. if (doubleBuffer.image == null) {
  547. result = c.createImage(width , height);
  548. doubleBuffer.size = new Dimension(width, height);
  549. if (c instanceof JComponent) {
  550. ((JComponent)c).setCreatedDoubleBuffer(true);
  551. doubleBuffer.image = result;
  552. }
  553. // JComponent will inform us when it is no longer valid
  554. // (via removeNotify) we have no such hook to other components,
  555. // therefore we don't keep a ref to the Component
  556. // (indirectly through the Image) by stashing the image.
  557. }
  558. return result;
  559. }
  560. /** Set the maximum double buffer size. **/
  561. public void setDoubleBufferMaximumSize(Dimension d) {
  562. doubleBufferMaxSize = d;
  563. if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) {
  564. if (standardDoubleBuffer.image.getWidth(null) > d.width ||
  565. standardDoubleBuffer.image.getHeight(null) > d.height) {
  566. standardDoubleBuffer.image = null;
  567. }
  568. }
  569. // Clear out the VolatileImages
  570. Iterator gcs = volatileMap.keySet().iterator();
  571. while (gcs.hasNext()) {
  572. GraphicsConfiguration gc = (GraphicsConfiguration)gcs.next();
  573. VolatileImage image = (VolatileImage)volatileMap.get(gc);
  574. if (image.getWidth() > d.width || image.getHeight() > d.height) {
  575. image.flush();
  576. gcs.remove();
  577. }
  578. }
  579. }
  580. /**
  581. * Returns the maximum double buffer size.
  582. *
  583. * @return a Dimension object representing the maximum size
  584. */
  585. public Dimension getDoubleBufferMaximumSize() {
  586. if (doubleBufferMaxSize == null) {
  587. try {
  588. doubleBufferMaxSize = Toolkit.getDefaultToolkit().getScreenSize();
  589. } catch (HeadlessException e) {
  590. doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  591. }
  592. }
  593. return doubleBufferMaxSize;
  594. }
  595. /**
  596. * Enables or disables double buffering in this RepaintManager.
  597. * CAUTION: The default value for this property is set for optimal
  598. * paint performance on the given platform and it is not recommended
  599. * that programs modify this property directly.
  600. *
  601. * @param aFlag true to activate double buffering
  602. * @see #isDoubleBufferingEnabled
  603. */
  604. public void setDoubleBufferingEnabled(boolean aFlag) {
  605. doubleBufferingEnabled = aFlag;
  606. }
  607. /**
  608. * Returns true if this RepaintManager is double buffered.
  609. * The default value for this property may vary from platform
  610. * to platform. On platforms where native double buffering
  611. * is supported in the AWT, the default value will be <code>false</code>
  612. * to avoid unnecessary buffering in Swing.
  613. * On platforms where native double buffering is not supported,
  614. * the default value will be <code>true</code>.
  615. *
  616. * @return true if this object is double buffered
  617. */
  618. public boolean isDoubleBufferingEnabled() {
  619. return doubleBufferingEnabled;
  620. }
  621. /**
  622. * This resets the double buffer. Actually, it marks the double buffer
  623. * as invalid, the double buffer will then be recreated on the next
  624. * invocation of getOffscreenBuffer.
  625. */
  626. void resetDoubleBuffer() {
  627. if (standardDoubleBuffer != null) {
  628. standardDoubleBuffer.needsReset = true;
  629. }
  630. }
  631. /**
  632. * This resets the volatile double buffer.
  633. */
  634. void resetVolatileDoubleBuffer(GraphicsConfiguration gc) {
  635. Image image = (Image)volatileMap.remove(gc);
  636. if (image != null) {
  637. image.flush();
  638. }
  639. }
  640. /**
  641. * Returns true if we should use the <code>Image</code> returned
  642. * from <code>getVolatileOffscreenBuffer</code> to do double buffering.
  643. */
  644. boolean useVolatileDoubleBuffer() {
  645. return volatileImageBufferEnabled;
  646. }
  647. private class DoubleBufferInfo {
  648. public Image image;
  649. public Dimension size;
  650. public boolean needsReset = false;
  651. }
  652. }