JDK-6255371 : REGRESSION: JDialog shows painting artifacts on first showing
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_nt
  • CPU: x86
  • Submitted: 2005-04-14
  • Updated: 2010-04-02
  • Resolved: 2005-11-30
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b62Fixed
Related Reports
Relates :  
Relates :  
Description
Compile and run the test case below. It shows a JFrame and then a JDialog. At the top and left of the JDialog, after it first appears, there are painting artifacts. The top row and left column of pixels aren't painted and the dialog shows colors from the desktop. This dissapears after forcing a repaint (such as by obscuring and then unobscuring the window). Attached, as artifacts.bmp, is a screen shot of the problem, which also shows the dialogs in question magnified to show the artifacts better.

Note: I believe this was introduced with the gray rect fix.

Source code for GR.java
----
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

public class GR extends JFrame {

    public GR() {
        super("GR");
    }
    
    private void showLogDialog() {
        JDialog d = new JDialog(this);
        d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        d.getContentPane().setBackground(Color.BLACK);
        d.setSize(100, 100);
        d.setVisible(true);
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                GR test = new GR();
                test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                test.setSize(300, 300);
                test.setLocationRelativeTo(null);
                test.setVisible(true);
                test.showLogDialog();
            }
        });
    }
}
###@###.### 2005-04-14 16:32:29 GMT

Comments
EVALUATION The current idea for the fix is to pass the Runnables that need to be processed through the PaintEventDispatcher. Swing's PaintEventDispatcher will override this method and forward the Runnable to the RepaintManager. The RepaintManager, before painting, will run all these Runnables. This way we are sure to replace the surface data before we paint.
17-10-2005

EVALUATION The fix that recreates surface data on the current thread is risky and causes regressions like the following hang: 2005-09-21 17:31:13 Full thread dump Java HotSpot(TM) Client VM (1.6.0-ea-b49 mixed mode): "DestroyJavaVM" prio=5 tid=0x00035b00 nid=0x764 waiting on condition [0x00000000..0x0007fb24] java.lang.Thread.State: RUNNABLE "TimerQueue" daemon prio=5 tid=0x0b16e000 nid=0xc7c in Object.wait() [0x0bc3f000..0x0bc3fb14] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x02fa2278> (a javax.swing.TimerQueue) at javax.swing.TimerQueue.run(TimerQueue.java:233) - locked <0x02fa2278> (a javax.swing.TimerQueue) at java.lang.Thread.run(Thread.java:608) "AWT-EventQueue-0" prio=7 tid=0x0b12bc00 nid=0x9ac runnable [0x0babf000..0x0babfb94] java.lang.Thread.State: RUNNABLE at sun.awt.windows.WComponentPeer.beginValidate(Native Method) at java.awt.Container.validate(Container.java:1440) - locked <0x02e550b0> (a java.awt.Component$AWTTreeLock) at sun.awt.windows.WComponentPeer$1.run(WComponentPeer.java:162) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209) at java.awt.EventQueue.dispatchEvent(EventQueue.java:597) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160) at java.awt.EventDispatchThread.run(EventDispatchThread.java:121) "AWT-Windows" daemon prio=7 tid=0x0b0e9500 nid=0x158 waiting for monitor entry [0x0b7fe000..0x0b7ffc94] java.lang.Thread.State: BLOCKED (on object monitor) at sun.awt.windows.WComponentPeer.replaceSurfaceData(WComponentPeer.java:342) - waiting to lock <0x02e550b0> (a java.awt.Component$AWTTreeLock) at sun.awt.windows.WComponentPeer.replaceSurfaceData(WComponentPeer.java:331) at sun.java2d.windows.Win32SurfaceData.getReplacement(Win32SurfaceData.java:421) at sun.java2d.SunGraphics2D.revalidateAll(SunGraphics2D.java:2288) at sun.java2d.SunGraphics2D.getCompClip(SunGraphics2D.java:474) at sun.java2d.pipe.DrawImage.renderImageCopy(DrawImage.java:509) at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:73) at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:974) at sun.java2d.pipe.ValidatePipe.copyImage(ValidatePipe.java:178) at sun.java2d.SunGraphics2D.copyImage(SunGraphics2D.java:2908) at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3046) at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3000) at java.awt.Component$BltBufferStrategy.showSubRegion(Component.java:3888) at java.awt.Component$BltSubRegionBufferStrategy.validateAndShow(Component.java:4023) at javax.swing.BufferStrategyPaintManager.show(BufferStrategyPaintManager.java:243) at javax.swing.RepaintManager.show(RepaintManager.java:1112) at javax.swing.SwingPaintEventDispatcher.createPaintEvent(SwingPaintEventDispatcher.java:43) at sun.awt.windows.WComponentPeer.postPaintIfNecessary(WComponentPeer.java:633) at sun.awt.windows.WComponentPeer.handleExpose(WComponentPeer.java:617) at sun.awt.windows.WToolkit.eventLoop(Native Method) at sun.awt.windows.WToolkit.run(WToolkit.java:290) at java.lang.Thread.run(Thread.java:608) "AWT-Shutdown" prio=5 tid=0x0b0c5700 nid=0xf30 in Object.wait() [0x0b6ff000..0x0b6ffd14] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x02e51828> (a java.lang.Object) at java.lang.Object.wait(Object.java:484) at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:259) - locked <0x02e51828> (a java.lang.Object) at java.lang.Thread.run(Thread.java:608) "Java2D Disposer" daemon prio=10 tid=0x0b0c5200 nid=0xefc in Object.wait() [0x0b5ff000..0x0b5ffd94] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x02e518b8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) - locked <0x02e518b8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) at sun.java2d.Disposer.run(Disposer.java:107) at java.lang.Thread.run(Thread.java:608) "Low Memory Detector" daemon prio=5 tid=0x0aaada00 nid=0xfb4 runnable [0x00000000..0x00000000] java.lang.Thread.State: RUNNABLE "CompilerThread0" daemon prio=10 tid=0x0aaac700 nid=0xb40 waiting on condition [0x00000000..0x0af6f8b8] java.lang.Thread.State: RUNNABLE "Attach Listener" daemon prio=10 tid=0x0aaaa600 nid=0xeec runnable [0x00000000..0x00000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" daemon prio=10 tid=0x0aaa9a00 nid=0x7d0 waiting on condition [0x00000000..0x00000000] java.lang.Thread.State: RUNNABLE "Finalizer" daemon prio=9 tid=0x0aa9d900 nid=0xd40 in Object.wait() [0x0ad2f000..0x0ad2fc94] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x02e51ae8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116) - locked <0x02e51ae8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159) "Reference Handler" daemon prio=10 tid=0x0aa9c300 nid=0xdc4 in Object.wait() [0x0ac2f000..0x0ac2fd14] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x02e51750> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:484) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) - locked <0x02e51750> (a java.lang.ref.Reference$Lock) "VM Thread" prio=10 tid=0x0aa97f00 nid=0x934 runnable "VM Periodic Task Thread" prio=10 tid=0x0aaaef00 nid=0xaa4 waiting on condition JNI global references: 1263 Here is what happens: EDT: Container.validate() acquires AWTTreeLock and then calls the native method WComponentPeer.beginValidate() TOOLKIT THREAD: BufferStrategyPaintManager.show() results in a call to WComponentPeer.replaceSurfaceData(), which waits to lock AWTTreeLock EDT: beginValidate() sends a message to the toolkit thread (EDT is blocked until the receiving thread processes the message). Thus we won't apply this fix.
06-10-2005

EVALUATION JDialog's insets are changed [awt_Window.cpp:UpdateInsets()] and a request to replace the surface data is scheduled from the toolkit thread to be invoked later on the EDT [WComponentPeer.replaceSurfaceDataLater()]. Just after this a native paint event comes and a corresponding paint request gets queued to be invoked later on the EDT [RepaintManager.nativeAddDirtyRegion()]. JFrame is shown before JDialog and a corresponding paint request gets queued to be invoked later on the EDT. When RepaintManager processes JFrame's request, it processes JDialog's request too, but JDialog's surface data are not up-to-date at the time. One way to fix this issue is to change RepaintManager so that it doesn't coalesce paint requests belonging to different Swing heavyweight components. ###@###.### thinks that it's risky for Swing. Another fix changes only Java2D/AWT code. Below are its details. Whenever SunGraphics2D sees that its surface data is invalid, it obtains new surface data via SurfaceData.getReplacement(). Win32SurfaceData.getReplacement() doesn't recreate surface data, it merely gets the current surface from the component peer. So this fix is to invalidate the surface data on the toolkit thread when the component's insets are changed [WComponentPeer.replaceSurfaceDataLater()] and create a new surface in Win32SurfaceData.getReplacement() if the current surface is invalid. WComponentPeer.replaceSurfaceData() acquires treeLock and adding one more place of treeLock usage may cause new deadlocks, but user code is responsible for such deadlocks as we can never ensure that user synchronization plays well with treeLock. See the suggested fix section for the code of this fix. A mail archive with a discussion of this issue is available at http://sa.sfbay.sun.com/mail-archive/swing-db/index.html (subject: 'about the bug 6255371').
12-08-2005

SUGGESTED FIX --- SurfaceData.java Fri Aug 12 17:37:06 2005 *************** *** 58,64 **** */ public abstract class SurfaceData implements Transparency, DisposerTarget { private long pData; ! private boolean valid; private boolean surfaceLost; // = false; private SurfaceType surfaceType; private ColorModel colorModel; --- 58,64 ---- */ public abstract class SurfaceData implements Transparency, DisposerTarget { private long pData; ! private volatile boolean valid; private boolean surfaceLost; // = false; private SurfaceType surfaceType; private ColorModel colorModel; --- Win32SurfaceData.java Thu Aug 4 18:49:01 2005 *************** *** 417,422 **** --- 417,425 ---- } public SurfaceData getReplacement() { + if (!isValid()) { + peer.replaceSurfaceData(); + } return peer.getSurfaceData(); } --- WComponentPeer.java Thu Aug 4 18:48:33 2005 *************** *** 369,388 **** } public void replaceSurfaceDataLater() { ! EventQueue.invokeLater(new Runnable() { ! public void run() { ! // Shouldn't do anything if object is disposed in meanwhile ! // No need for sync as disposeAction in Window is performed ! // on EDT ! if (!isDisposed()) { ! try { ! replaceSurfaceData(); ! } catch (InvalidPipeException e) { ! // REMIND : what do we do if our surface creation failed? ! } ! } ! } ! }); } /** --- 369,380 ---- } public void replaceSurfaceDataLater() { ! SurfaceData sd = surfaceData; ! if (sd != null) { ! sd.invalidate(); ! } ! // SunGraphics2D will call Win32SurfaceData.getReplacement() ! // to create new surface data later whenever needed. } /**
12-08-2005

EVALUATION It appears as though the coordinates passed to the PaintEventDispatcher in this case aren't right. Here's how to check this. Change javax.swing.BufferStrategyPaintManager.show to have (as the first thing in the method): Graphics g = c.getGraphics(); g.setColor(Color.RED); g.fillRect(x, y, w, h); if (true) { return true; } This will force Swing to fill in the background of the exposed region to red and not do any painting. If you do this change you still see the garbage, which seems to indicate that either the region passed to the PaintEventDispatcher is wrong, or that the surface isn't right. Reassigning to AWT for further investigation. Also note that if you change the test case not to create a JFrame, just a JDialog, the garbage isn't there. ###@###.### 2005-04-16 00:01:09 GMT The native AWT system provides the paint event dispatcher with the same rectangle to paint on first appearance of JDialog in both cases: JFrame, JDialog with painting artifacts and only JDialog without painting artifacts; insets of JDialog are the same too. ###@###.### 2005-04-22 16:23:17 GMT If I specify the system property -Dswing.handleTopLevelPaint=false, the bug is not reproducible. ###@###.### 2005-07-14 16:51:45 GMT
14-04-2005