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();
            }
        });
    }
}
