JDK-8162350 : RepaintManager shifts repainted region when the floating point UI scale is used
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2016-07-21
  • Updated: 2024-09-18
  • Resolved: 2016-12-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b150Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
- Run the CustomSelectedComponent sample below which draws 4 letters and sets UI scale to floating point value 1.5 
- Click mouse on the second letter.
The letter is repainted with different color.
The resulted repainted letter is shifted to one pixel left.

See the screenshots:
custom-component.png - initial component
custom-component-selected - the second letter is repainted and shifted to one pixel left

------------------
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class CustomSelectedComponent {

    private static final int W = 40;
    private static final int H = 60;
    private static float SCALE = 1.5f;

    static class CustomPanel extends JPanel {

        static final Color DRAW_COLOR = Color.PINK;
        static final Color REDRAW_COLOR = Color.MAGENTA;
        final int w;
        final int h;
        boolean redraw = false;

        public CustomPanel(int w, int h) {
            super(new FlowLayout());
            this.w = w;
            this.h = h;

            addMouseListener(new MouseAdapter() {

                @Override
                public void mousePressed(MouseEvent e) {
                    int x = e.getX();
                    int y = e.getY();
                    int xx = 0;
                    int yy = 0;
                    if (x < w) {
                        redraw = true;
                    } else if (x < 2 * w) {
                        redraw = true;
                        xx = w;
                        yy = 0;
                    } else {
                        redraw = false;
                    }

                    if (redraw) {
                        repaint(xx - 1, yy - 1, w + 2, h + 2);
                    } else {
                        repaint();
                    }
                }
            });
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            int w2 = 2 * w;
            int h2 = 2 * h;

            g.setColor(redraw ? REDRAW_COLOR : DRAW_COLOR);
            g.drawLine(0, 0, w2, 0);
            g.drawLine(0, 0, 0, h2);
            g.drawLine(w2, 0, w2, h2);
            g.drawLine(0, h2, w2, h2);
            g.drawLine(w, 0, w, h2);
            g.drawLine(0, h, w2, h);
            paintLetter(g, 0, 0);
            paintLetter(g, w, 0);
            paintLetter(g, 0, h);
            paintLetter(g, w, h);
        }

        private void paintLetter(Graphics g, int x, int y) {
            int w_2 = w / 2;
            int w_4 = w / 4;
            int h_2 = h / 2;
            g.drawLine(x + w_2, y, x, y + h);
            g.drawLine(x + w_2, y, x + w, y + h);
            g.drawLine(x + w_4, y + h_2, x + w_4 + w_2, y + h_2);
        }
    }

    public static void main(String[] args) {

        System.setProperty("sun.java2d.uiScale", Float.toString(SCALE));

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            frame.setSize(300, 300);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new CustomPanel(W, H);
            frame.getContentPane().add(panel);
            frame.setVisible(true);
        });
    }
}
------------------
Comments
URL: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/067f28de477e User: lana Date: 2016-12-21 16:40:05 +0000
21-12-2016

URL: http://hg.openjdk.java.net/jdk9/client/jdk/rev/067f28de477e User: alexsch Date: 2016-12-12 18:48:08 +0000
12-12-2016

Approved by component triage team to defer
07-09-2016

SQE: JDK9: OK to defer
07-09-2016

Just a calculation for a component location which can have even and odd value. Let's a component coordinates are [x, y] in the user space and [xd, yd] = [x * scale, y * scale] in the dev space. Component region relative coordinates are [rx, ry], absolute coordinates: [arx, ary] = [x + rx, y+ry] in the user space and [arxd, aryd] = [(x + rx) * scale, (y+ry) * scale] in the dev space The problem is reproduced when the component relative position is odd: rx = 2*rn + 1 and the UI scale has floating point value 1.5. a) the component position is even: x = 2 * n xd = x * scale = 2 * n * 1.5 = 3 * n arxd = (x + rx) * scale = (2 * n + 2*rn + 1) * 1.5 = 3 * (n + rn) + 1.5 which is rounded to 3 * (n + rn) + 2 the difference between the component and its region in the dev space is arxd - xd = 3 * (n + rn) + 2 - 3 * n = 3 * rn + 2 b) the component position is odd: x = 2 * n + 1 xd = x * scale = (2 * n + 1) * 1.5 = 3 * n + 1.5 which is rounded to 3 *n + 2 arxd = (x + rx) * scale = ((2 * n + 1) + (2*rn + 1)) * 1.5 = 3 * (n + rn + 1) the difference between the component and its region in the dev space is arxd - xd = 3 * (n + rn + 1) - (3 * n + 2) = 3 * rn + 1 The difference between the component and its region in the dev space depends on parity of the component position: 3 * rn + 2 for even component position 3 * rn + 1 for odd component position Which leads that a component subregion redrawing can be shifted to one pixel.
22-07-2016

Let's have a component with location [x, y] size [20, 10] where a text is drawn and it needs to redraw only a part of the component with relative to the component coordinates [13, 0] and size [7, 10]. The RepaintManager draws the component image from coordinates x * scale in the dev space. The component region is drawn from coordinates (x + 13) * scale. For the x = 0 and UI scale = 1.5: The component is drawn from position x * scale = 0 * 1.5 = 0. The component region position is (x + 13) * 1.5 = 13 * 1.5 = 19.5 which is rounded to 20. The distance from the first image to the second is 20 - 0 = 20. For the x = 1 and UI scale = 1.5: The component is drawn from position x * scale = 1 * 1.5 = 1.5 which is rounded to 2. The component region position is (x + 13) * 1.5 = 14 * 1.5 = 21. The distance from the first image to the second is 21 - 2 = 19. As it is shown the distance between the component image and its region is 20 in the first case and 19 in the second. That leads that the repainted region can be shifted to the left on one pixel.
21-07-2016