On Sun, 12 Mar 2023 17:35:40 GMT, Jeremy <[email protected]> wrote:

> I'm confident about the new test case for this ticket, but the resolution is 
> more invasive than I'd like. (It works, though.)
> 
> In short:
> This introduces a new RenderingHint (in SunHints) to bypass the call in 
> Window to `gg2d.fillRect(0, 0, getWidth(), getHeight());`
> 
> I left more detailed notes here about the proposed resolution:
> https://github.com/openjdk/jdk/commit/1991fdac5dbf76ddaf73cc78a9f7c38370c9674c
> 
> I'm open to suggestions if anyone has a more elegant proposal to prevent the 
> monitor from refreshing too soon?

> I would like to clarify two question:
> 
>  1. I think most of the opaque/non-transparent components fill the background 
> by the opaque color(including windows/frames/etc), why the problem is not 
> reproduced in that case?

Great question. (I wasn't sure, so I had to explore this.)

The RepaintManager interprets a repaint request for any given JComponent and 
finds its nearest opaque ancestor to repaint.

I modified my test so the JLabel is opaque and added a debugger breakpoint in 
RepaintManager.paintDirtyRegions. RepaintManager enters this block of code:

                        if (dirtyComponent instanceof JComponent) {
                            ((JComponent)dirtyComponent).paintImmediately(
                                rect.x,rect.y,rect.width, rect.height);
                        }
                        else if (dirtyComponent.isShowing()) {
                            Graphics g = JComponent.safelyGetGraphics(
                                    dirtyComponent, dirtyComponent);
                            // If the Graphics goes away, it means someone 
disposed of
                            // the window, don't do anything.
                            if (g != null) {
                                g.setClip(rect.x, rect.y, rect.width, 
rect.height);
                                try {
                                    dirtyComponent.paint(g);
                                } finally {
                                    g.dispose();
                                }
                            }
                        }


So when my JLabel was opaque: we entered the first branch (where 
`dirtyComponent` is the JLabel). Here Swing tightly controls this process, and 
eventually we call `c.paintToOffscreen`, where c is the JLabel and the 
Graphics2D is a reference to RepaintManager's offscreen image. Once the 
JComponent is finished, then the PaintManager calls:

                            final Graphics2D g2d = (Graphics2D) g;
                            final Composite oldComposite = g2d.getComposite();
                            g2d.setComposite(AlphaComposite.Src);
                            g2d.drawImage(image, x, y, c);
                            g2d.setComposite(oldComposite);

This time `g` is the Window's Graphics2D that relates to the actual surface 
data on the monitor.

Specifically the stacktrace I'm seeing in JDK 19 when I repainted my opaque 
JLabel is:

paintDoubleBufferedImpl:1663, RepaintManager$PaintManager (javax.swing)
paintDoubleBuffered:1631, RepaintManager$PaintManager (javax.swing)
paint:1569, RepaintManager$PaintManager (javax.swing)
paint:1336, RepaintManager (javax.swing)
_paintImmediately:5266, JComponent (javax.swing)
paintImmediately:5076, JComponent (javax.swing)
run:878, RepaintManager$4 (javax.swing)
run:861, RepaintManager$4 (javax.swing)
executePrivileged:776, AccessController (java.security)
doPrivileged:399, AccessController (java.security)
doIntersectionPrivilege:86, ProtectionDomain$JavaSecurityAccessImpl 
(java.security)
paintDirtyRegions:861, RepaintManager (javax.swing)
paintDirtyRegions:834, RepaintManager (javax.swing)
prePaintDirtyRegions:784, RepaintManager (javax.swing)
run:1897, RepaintManager$ProcessingRunnable (javax.swing)
dispatch$$$capture:318, InvocationEvent (java.awt.event)
dispatch:-1, InvocationEvent (java.awt.event)


>  2. Why the double-buffer in the RepaintManager does not handle this? If no 
> buffers are used, then probably we should have one to render everything to it 
> and then blit to Window?

In the original unit test: the JLabel is not opaque, so the `dirtyComponent` we 
set out to repaint was our root JWindow. So we entered the second branch in the 
code above and called `dirtyComponent.paint(g)`.

Once we entered Swing rendering code: all our rendering really was 
double-buffered to the appropriate offscreen image. But the problem is 
Window.java has that one call to `fillRect` that is outside of Swing's 
rendering model. (So in a way this is a subtle clash of Swing vs AWT code.)

-------------

PR: https://git.openjdk.org/jdk/pull/12993

Reply via email to