EVALUATION
The requested feature is not supported by design,
closed
|
|
|
WORK AROUND
Turn off double buffering for all the components painted out of order (i.e. not from their paint()/paintComponent() methods).
|
|
|
EVALUATION
This bug is about something that Swing painting system is not supported
from the beginning, in other words the Swing painting is not re-entrant
and breaking the common painting mechanism by calling paint() of stranger component
inside another component's paint() leads to unpredictable result
I submitted this bug some time ago but now I can see that
there is no serious reasons to spend lots of time for this,
because what we actually need is to fix 6683775
for which we found an elegant and robust fix
this bug is downgraded to P5, 6683775 is reopened
|
|
|
SUGGESTED FIX
diff -r d5bf2dd61ed5 src/share/classes/javax/swing/BufferStrategyPaintManager.java
--- a/src/share/classes/javax/swing/BufferStrategyPaintManager.java Fri Dec 19 16:04:04 2008 +0300
+++ b/src/share/classes/javax/swing/BufferStrategyPaintManager.java Mon Dec 22 13:38:51 2008 +0300
@@ -513,8 +513,8 @@ class BufferStrategyPaintManager extends
bsg.dispose();
bsg = null;
}
- bufferStrategy = null;
if (fetchRoot(c)) {
+ bufferStrategy = null;
boolean contentsLost = false;
BufferInfo bufferInfo = getBufferInfo(root);
if (bufferInfo == null) {
@@ -576,19 +576,21 @@ class BufferStrategyPaintManager extends
private boolean fetchRoot(JComponent c) {
boolean encounteredHW = false;
- rootJ = c;
- root = c;
- xOffset = yOffset = 0;
- while (root != null && (!(root instanceof Window) &&
- !(root instanceof Applet))) {
- xOffset += root.getX();
- yOffset += root.getY();
- root = root.getParent();
- if (root != null) {
- if (root instanceof JComponent) {
- rootJ = (JComponent)root;
+ // Fix for 6668436: use local vars for root, rootJ and x/yOffset and copy
+ // their values to corresponding fields only on successful search.
+ Container tempRoot = c;
+ JComponent tempRootJ = c;
+ int tempxOffset = 0, tempyOffset = 0;
+ while (tempRoot != null && (!(tempRoot instanceof Window) &&
+ !(tempRoot instanceof Applet))) {
+ tempxOffset += tempRoot.getX();
+ tempyOffset += tempRoot.getY();
+ tempRoot = tempRoot.getParent();
+ if (tempRoot != null) {
+ if (tempRoot instanceof JComponent) {
+ tempRootJ = (JComponent)tempRoot;
}
- else if (!root.isLightweight()) {
+ else if (!tempRoot.isLightweight()) {
if (!encounteredHW) {
encounteredHW = true;
}
@@ -608,17 +610,21 @@ class BufferStrategyPaintManager extends
}
}
}
- if ((root instanceof RootPaneContainer) &&
- (rootJ instanceof JRootPane)) {
+ if ((tempRoot instanceof RootPaneContainer) &&
+ (tempRootJ instanceof JRootPane)) {
// We're in a Swing heavyeight (JFrame/JWindow...), use double
// buffering if double buffering enabled on the JRootPane and
// the JRootPane wants true double buffering.
- if (rootJ.isDoubleBuffered() &&
- ((JRootPane)rootJ).getUseTrueDoubleBuffering()) {
+ if (tempRootJ.isDoubleBuffered() &&
+ ((JRootPane)tempRootJ).getUseTrueDoubleBuffering()) {
// Whether or not a component is double buffered is a
// bit tricky with Swing. This gives a good approximation
// of the various ways to turn on double buffering for
// components.
+ xOffset = tempxOffset;
+ yOffset = tempyOffset;
+ root = tempRoot;
+ rootJ = tempRootJ;
return true;
}
}
|
|
|
EVALUATION
Here is a rough sequence of calls causing NPE:
1. RepaintManager processes all the dirty components on EDT and calls (indirectly) paint() for JButton b.
2. Button paints itself with the call super.paint(g).
3. super.paint(g) calls RepaintManager's methods beginPaint, paint, endPaint - see JComponent.paint() (~line 1024).
4. RepaintManager.paint() forwards all the painting to its paintManager, which is an instance of BufferStrategyPaintManager (BSPM).
5. BSPM.paint() successfully paints the button into its bufferStrategy.
6. Button paints the panel to offscreen image - see the test code.
7. Steps 3-4 are repeated for the panel.
8. BSPM.paint() calls to prepare(), which nullify 'bufferStrategy' field and calls to fetchRoot(). The latter method returns false, because the panel is outside of any Swing hierarchy, and default paint() is used instead of using bufferStrategy.
Here the problem is: while traversing all the parents for component in fetchRoot(), BSPM fields 'root', 'rootJ' and 'xOffset/yOffset' are used and modified(!) regardless of the (future) return value of fetchRoot(). The same problem observed in prepare(): 'bufferStrategy' is nullified regardless of return value from fetchRoot().
9. RepaintManager.endPaint() forwards to BSPM.endPaint(), which calls to flushAccumulatedRegion().
10. flushAccumulatedRegion throws NPE as 'bufferStrategy' field is null.
|
|
|
EVALUATION
I see the following exception in console when running the test with 7.0-b42:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.BufferStrategyPaintManager.flushAccumulatedRegion(BufferStrategyPaintManager.java:423)
at javax.swing.BufferStrategyPaintManager.endPaint(BufferStrategyPaintManager.java:387)
at javax.swing.RepaintManager.endPaint(RepaintManager.java:1253)
at javax.swing.JComponent.paint(JComponent.java:1031)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:78)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:115)
at java.awt.Container.paint(Container.java:1785)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:786)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:731)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:711)
at javax.swing.RepaintManager.access$700(RepaintManager.java:59)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1594)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:235)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:603)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:286)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:191)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:186)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:178)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:139)
NPE is caused by null 'bufferStrategy' field in BufferStrategyPaintManager - see flushAccumulatedRegion method for details.
|
|
|
EVALUATION
We should switch off the doulbe buffering
when painting to an off-screen image
|
|
|
|