Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8292976

Inconsistent Swing component repaint origin with fractional scaling

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Windows 11, jdk-17.0.4.1+1

      A DESCRIPTION OF THE PROBLEM :
      With Windows set to a fractional scaling (125%), Graphics2D operations are done at inconsistent locations depending on the levels that get repainted. This happens for certain component heights, and certain component locations.
      The result for the end-user is that a component is painted, then its state changes and is repainted but the bottom pixels are not updated, leaving ugly traces on screen.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Using the provided test case:
      - Press "Repaint all" to repaint the whole UI, and the component gets painted in black.
      - Press "Repaint component" to only repaint the component, and the component gets painted in white.

      It is possible to increase the top border in case on your setup a different location is needed to trigger the bug.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The component should be completely white.
      ACTUAL -
      The component is white except for the bottom line that remained black.

      See the error in this image: <link>

      ---------- BEGIN SOURCE ----------
      package test.graphics2d;

      import java.awt.BorderLayout;
      import java.awt.Color;
      import java.awt.Container;
      import java.awt.Dimension;
      import java.awt.FlowLayout;
      import java.awt.Graphics;
      import java.awt.event.MouseAdapter;
      import java.awt.event.MouseEvent;

      import javax.swing.BorderFactory;
      import javax.swing.BoxLayout;
      import javax.swing.JButton;
      import javax.swing.JComponent;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JPanel;

      public class Graphics2DTestOnHighDpi {

          private static int borderSize = 8;
          private static boolean isRepaintComponent;

      public static void main(String[] args) {
      JFrame frame = new JFrame("Graphics2D Test with fractional scaling");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      Container contentPane = frame.getContentPane();
      JPanel northPane = new JPanel(new FlowLayout());
      northPane.setBorder(BorderFactory.createEmptyBorder(borderSize, 0, 0, 0));
              JPanel componentContainer = new JPanel();
              componentContainer.setLayout(new BoxLayout(componentContainer, BoxLayout.LINE_AXIS));
              JComponent component = new JComponent() {
                  @Override
                  protected void paintComponent(Graphics g) {
                      g.setColor(isRepaintComponent? Color.WHITE: Color.BLACK);
                      g.fillRect(0, 0, getWidth(), getHeight());
                      isRepaintComponent = false;
                  }
              };
              component.setPreferredSize(new Dimension(50, 32));
              componentContainer.add(component);
      northPane.add(componentContainer, BorderLayout.CENTER);
              contentPane.add(northPane, BorderLayout.NORTH);
              JPanel southPane = new JPanel(new FlowLayout());
              JButton increaseBorderButton = new JButton("Increase top margin");
              increaseBorderButton.addActionListener((ae) -> northPane.setBorder(BorderFactory.createEmptyBorder(++borderSize, 0, 0, 0)));
              southPane.add(increaseBorderButton);
              JLabel repaintComponentButton = new JLabel("Repaint Component");
              repaintComponentButton.setBorder(BorderFactory.createLineBorder(Color.BLACK));
              repaintComponentButton.addMouseListener(new MouseAdapter() {
                  @Override
                  public void mousePressed(MouseEvent e) {
                      isRepaintComponent = true;
                      component.repaint();
                  }
              });
              southPane.add(repaintComponentButton);
              JLabel repaintAllButton = new JLabel("Repaint all");
              repaintAllButton.setBorder(BorderFactory.createLineBorder(Color.BLACK));
              repaintAllButton.addMouseListener(new MouseAdapter() {
                  @Override
                  public void mousePressed(MouseEvent e) {
                      frame.repaint();
                  }
              });
              southPane.add(repaintAllButton);
              contentPane.add(southPane, BorderLayout.SOUTH);
      frame.setSize(400, 300);
      frame.setVisible(true);
      }

      }

      ---------- END SOURCE ----------

      FREQUENCY : always


            prr Philip Race
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: