javaswingscalinggraphics2dscreen-resolution

Java Swing Graphics are drawned inconsistently when system scale is 125% in a scrollpane


at 100% screen scaling the drawn shape is always "pixel perfect".

enter image description here

at 125% screen scaling the same shape in this case it's sometimes drawn as if it has an extra row of pixels on the top (1) instead of the way it should be drawned (2). These shapes are drawned in a list in a scrollpane and while scrolling up and down, they flicker, changing from one version to the other and it's very apparent and distracting.

enter image description here

This I've observed being dependant on the component position, and perhaps I suppose it draws the shape inbetween 2 pixels and so it "randomly" chooses either the pixel above for one component or the one below for another one...

package debug;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;

public class TestBugScaling implements Runnable {
    
    private final int size = 38;
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new TestBugScaling());
    }
    
    @Override
    public void run() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setPreferredSize(new Dimension(400, 400));
        
        Container contentPane = frame.getContentPane();
        contentPane.setLayout(new BorderLayout());
        contentPane.setPreferredSize(new Dimension(400, 400));
        
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setPreferredSize(new Dimension(size + 10, 400));
        scrollPane.getVerticalScrollBar().setUnitIncrement(size + 5);
        scrollPane.getVerticalScrollBar().setBlockIncrement((size + 5) * 2);
        
        JPanel container = new JPanel();
        container.setBorder(new EmptyBorder(5, 5, 5, 5));
        container.setLayout(new GridLayout(20, 0, 0, 5));
        container.setPreferredSize(new Dimension(size + 10, (size + 5) * 20));
        
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        container.add(new CirclePanel());
        
        scrollPane.setViewportView(container);
        
        contentPane.add(scrollPane, BorderLayout.NORTH);
        
        frame.pack();
        frame.setVisible(true);
    }

    public class CirclePanel extends JPanel {

        public CirclePanel() {
            super();
            setPreferredSize(new Dimension(size, size));
            setMinimumSize(new Dimension(size, size));
            setMaximumSize(new Dimension(size, size));
        }

        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g.create();
            g2.fillOval(0, 0, getHeight(), getHeight());
            g2.dispose();
        }

    }

}

With this code, if you have 100% screen scale, you will see a smooth scrolling of the circles. Instead, with a 125% screen scale, if you look closely you will see the circles bobbing up and down 1 pixel out of place while scrolling...

Is there a way to prevent / fix this? what is causing it?

I don't mind the shape not being completely perfect, but I do mind that it flickers and that it's not consistent all the time...


Solution

  • I found the problem... and more importantly I found a solution!

    The Graphics and Graphics2D objects needs some margin in order to draw curved shapes correctly, which in hindsight actually makes quite sense...

    By simply replacing g2.fillOval(0, 0, getHeight(), getHeight()); with g2.fillOval(1, 1, getHeight() - 2, getHeight() - 2); giving it a 1 unit spacing around the shape, it gives it enough space to deal with antialiasing and it automatically fixes the glitch I was experiencing...