Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HiDPI: fix incomplete component repainting at 125% or 175% scaling on Windows #864

Merged
merged 3 commits into from
Jul 15, 2024

Conversation

DevCharly
Copy link
Collaborator

@DevCharly DevCharly commented Jul 5, 2024

There is a problem in Swing, when using scale factors that end on .25 or .75
(e.g. 1.25, 1.75, 2.25, etc) and repainting single components.
Sometimes, under special conditions, the right and/or bottom 1px edge of the component is not repainted.

Following screenshot shows examples where the right and/or bottom 1px edge of a component was not repainted:

grafik

This PR introduces new repaint methods that fix/workaround the issue:

HiDPIUtils.repaint( Component c );
HiDPIUtils.repaint( Component c, Rectangle r );
HiDPIUtils.repaint( Component c, int x, int y, int width, int height );

fixes issues #860 and #582

Repaint Manager

There is also a repaint manager, that also fixes/workarounds the issue,
but does not need any change to existing code (no need to use HiDPIUtils.repaint()).
This can be useful for custom or 3rd party components.
Invoke following on application startup to use it:

HiDPIUtils.installHiDPIRepaintManager();

Be careful if already using a custom repaint manager,
or if using a library that may use a custom repaint manager (e.g. SwingX for translucent JXPanel).

In-depth analysis of the Swing issue

When repainting a component using Component.repaint(),
the component is first painted to an in-memory image,
and then that image is copied to the screen.
See javax.swing.RepaintManager.PaintManager.paintDoubleBufferedFPScales().

There are two clipping rectangles involved when copying the image to the screen:
sun.java2d.SunGraphics2D.devClip and sun.java2d.SunGraphics2D.usrClip.

devClip is the device clipping in physical pixels.
It gets the bounds of the painting component, which is either the passed component,
or if it is non-opaque, then the first opaque ancestor of the passed component.
It is calculated in sun.java2d.SunGraphics2D.constrain() while
getting a graphics context via JComponent.getGraphics().

usrClip is the user clipping, which is set via Graphics clipping methods.
This is done in javax.swing.RepaintManager.PaintManager.paintDoubleBufferedFPScales().

The intersection of devClip and usrClip
(computed in sun.java2d.SunGraphics2D.validateCompClip())
is used to copy the image to the screen.

Unfortunately different scaling/rounding strategies are used to calculate
the two clipping rectangles, which is the reason of the issue.

devClip (see sun.java2d.SunGraphics2D.constrain()):

int devX = (int) (x * scale);
int devWidth = Math.round( width * scale )

usrClip (see javax.swing.RepaintManager.PaintManager.paintDoubleBufferedFPScales()):

int usrX = (int) Math.ceil( (x * scale) - 0.5 );
int usrWidth = ((int) Math.ceil( ((x + width) * scale) - 0.5 )) - usrX;

X/Y coordinates are always rounded down for devClip, but rounded up for usrClip.
Width/height calculation is also different.

Conclusion

devClip is sometimes 1px too small.

Workaround

Do repaint on an (larger) ancestor.
In this case, devClip is larger because it gets the bounds of the ancestor.

@DevCharly DevCharly added this to the 3.5 milestone Jul 5, 2024
@DevCharly DevCharly changed the title HiDPI: fix incomplete component paintings at 125% or 175% scaling on Windows HiDPI: fix incomplete component repainting at 125% or 175% scaling on Windows Jul 5, 2024
…mponent paintings at 125% or 175% scaling on Windows (issues #860 and #582)
@DevCharly
Copy link
Collaborator Author

Update: added "Repaint Manager" section (see above)

@DevCharly DevCharly merged commit 1238da5 into main Jul 15, 2024
8 checks passed
@DevCharly DevCharly deleted the hidpi-repaint-fix branch July 15, 2024 16:51
DevCharly added a commit that referenced this pull request Aug 4, 2024
@DevCharly
Copy link
Collaborator Author

Update: there is a bug in HiDPIUtils.installHiDPIRepaintManager() in 3.5.
Upgrading to 3.5.1 is recommended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant