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