-
Bug
-
Resolution: Unresolved
-
P3
-
9, 11, 17, 21, 24, 25, 26
ADDITIONAL SYSTEM INFORMATION :
Windows 11 24H2
A DESCRIPTION OF THE PROBLEM :
Java seems to paint the background of a component in two different ways depending on when the painting is done. It looks like a rounding issue when the display scaling is greater than 100%. The difference is a 1 pixel line on the right or at the bottom of the component that is not repainted. One consequence is trailing segments appearing when components are dragged across a container.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Set the display scaling to 125%.
Create a JPanel with black background in a JFrame at (x, y), with width and height 40.
Through a Timer (10 ms is enough), change the background to white and call the repaint() method of the panel.
Try with different values for (x,y): (0,0), (0,1), (0,2)...
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The panel should be a white square.
ACTUAL -
Depending on the values of (x,y), black 1 pixel width lines appear at the right and/or at the bottom of the square.
Note that there is a regular pattern in the way the lines appear as you increase the x and y values.
---------- BEGIN SOURCE ----------
package HDPI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class HdpiPaintIssue extends JFrame {
public class TestPanel extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
updateInfo((Graphics2D) g);
}
private void updateInfo(Graphics2D g2d) {
Integer X = getX();
Integer Y = getY();
labelX.setText("x = " + X.toString());
labelY.setText("y = " + Y.toString());
final AffineTransform t = g2d.getTransform();
final Double tx = t.getTranslateX();
final Double ty = t.getTranslateY();
labelTX.setText("tx = " + tx.toString());
labelTY.setText("ty = " + ty.toString());
}
}
private final Color light = Color.white;
private final Color dark = Color.black;
private TestPanel panel = null;
private JLabel labelX = null;
private JLabel labelY = null;
private JLabel labelTX = null;
private JLabel labelTY = null;
public HdpiPaintIssue() {
super("HDPI issue");
setSize(250, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setVisible(true);
setFocusable(true);
requestFocus();
getContentPane().setLayout(null);
// Labels to display a few values
labelX = new JLabel();
labelX.setBounds(100, 2, 40, 24);
getContentPane().add(labelX);
labelY = new JLabel();
labelY.setBounds(100, 14, 40, 24);
getContentPane().add(labelY);
labelTX = new JLabel();
labelTX.setBounds(150, 2, 100, 24);
getContentPane().add(labelTX);
labelTY = new JLabel();
labelTY.setBounds(150, 14, 100, 24);
getContentPane().add(labelTY);
JLabel lbVersion = new JLabel("java.version = " + System.getProperty("java.version"));
lbVersion.setBounds(50, 140, 120, 24);
getContentPane().add(lbVersion);
// Panel to be moved
panel = new TestPanel();
panel.setBounds(0, 0, 40, 40);
panel.setOpaque(true);
panel.setBackground(dark);
getContentPane().add(panel);
delayedRepaint();
// Buttons to move the panel
addButton("<", 80, 80);
addButton(">", 140, 80);
addButton("^", 110, 50);
addButton("v", 110, 110);
}
private void addButton(String text, int x, int y) {
JButton b = new JButton(text);
b.setBounds(x, y, 50, 30);
getContentPane().add(b);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Point pt = panel.getLocation();
switch(text) {
case "<":
pt.x = pt.x - 1;
break;
case ">":
pt.x = pt.x + 1;
break;
case "^":
pt.y = pt.y - 1;
break;
default:
pt.y = pt.y + 1;
}
panel.setBackground(dark);
panel.setLocation(pt); // <- setLocation() triggers a dark painting
delayedRepaint(); // <- we trigger a light painting on top of it
}
});
}
private void delayedRepaint() {
Timer timer = new Timer(10, new ActionListener() { // A short delay is required for the issue to appear
@Override
public void actionPerformed(ActionEvent arg0) {
panel.setBackground(light);
panel.repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new HdpiPaintIssue();
}
});
}
}
---------- END SOURCE ----------
Windows 11 24H2
A DESCRIPTION OF THE PROBLEM :
Java seems to paint the background of a component in two different ways depending on when the painting is done. It looks like a rounding issue when the display scaling is greater than 100%. The difference is a 1 pixel line on the right or at the bottom of the component that is not repainted. One consequence is trailing segments appearing when components are dragged across a container.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Set the display scaling to 125%.
Create a JPanel with black background in a JFrame at (x, y), with width and height 40.
Through a Timer (10 ms is enough), change the background to white and call the repaint() method of the panel.
Try with different values for (x,y): (0,0), (0,1), (0,2)...
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The panel should be a white square.
ACTUAL -
Depending on the values of (x,y), black 1 pixel width lines appear at the right and/or at the bottom of the square.
Note that there is a regular pattern in the way the lines appear as you increase the x and y values.
---------- BEGIN SOURCE ----------
package HDPI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class HdpiPaintIssue extends JFrame {
public class TestPanel extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
updateInfo((Graphics2D) g);
}
private void updateInfo(Graphics2D g2d) {
Integer X = getX();
Integer Y = getY();
labelX.setText("x = " + X.toString());
labelY.setText("y = " + Y.toString());
final AffineTransform t = g2d.getTransform();
final Double tx = t.getTranslateX();
final Double ty = t.getTranslateY();
labelTX.setText("tx = " + tx.toString());
labelTY.setText("ty = " + ty.toString());
}
}
private final Color light = Color.white;
private final Color dark = Color.black;
private TestPanel panel = null;
private JLabel labelX = null;
private JLabel labelY = null;
private JLabel labelTX = null;
private JLabel labelTY = null;
public HdpiPaintIssue() {
super("HDPI issue");
setSize(250, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setVisible(true);
setFocusable(true);
requestFocus();
getContentPane().setLayout(null);
// Labels to display a few values
labelX = new JLabel();
labelX.setBounds(100, 2, 40, 24);
getContentPane().add(labelX);
labelY = new JLabel();
labelY.setBounds(100, 14, 40, 24);
getContentPane().add(labelY);
labelTX = new JLabel();
labelTX.setBounds(150, 2, 100, 24);
getContentPane().add(labelTX);
labelTY = new JLabel();
labelTY.setBounds(150, 14, 100, 24);
getContentPane().add(labelTY);
JLabel lbVersion = new JLabel("java.version = " + System.getProperty("java.version"));
lbVersion.setBounds(50, 140, 120, 24);
getContentPane().add(lbVersion);
// Panel to be moved
panel = new TestPanel();
panel.setBounds(0, 0, 40, 40);
panel.setOpaque(true);
panel.setBackground(dark);
getContentPane().add(panel);
delayedRepaint();
// Buttons to move the panel
addButton("<", 80, 80);
addButton(">", 140, 80);
addButton("^", 110, 50);
addButton("v", 110, 110);
}
private void addButton(String text, int x, int y) {
JButton b = new JButton(text);
b.setBounds(x, y, 50, 30);
getContentPane().add(b);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Point pt = panel.getLocation();
switch(text) {
case "<":
pt.x = pt.x - 1;
break;
case ">":
pt.x = pt.x + 1;
break;
case "^":
pt.y = pt.y - 1;
break;
default:
pt.y = pt.y + 1;
}
panel.setBackground(dark);
panel.setLocation(pt); // <- setLocation() triggers a dark painting
delayedRepaint(); // <- we trigger a light painting on top of it
}
});
}
private void delayedRepaint() {
Timer timer = new Timer(10, new ActionListener() { // A short delay is required for the issue to appear
@Override
public void actionPerformed(ActionEvent arg0) {
panel.setBackground(light);
panel.repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new HdpiPaintIssue();
}
});
}
}
---------- END SOURCE ----------