[OpenJDK 2D-Dev] HiDPI support issues on Windows

Anton Tarasov anton.tarasov at jetbrains.com
Wed Sep 28 10:17:54 UTC 2016


Hello,

JDK9 comes with HiDPI support on Windows/Linux which is really great. As 
we gave it a try, we found it looking pretty good with an integer scale 
(2x) but revealed some rendering flaws with float scales.

Let me please demonstrate it with SwingSet2 + JDK9-ea-b137 + Windows 8.1 
in 150% scale (1.5f)

demo1 <http://cr.openjdk.java.net/%7Eant/hidpi_pics/demo1.png>

Dragging Frame-0 behind the pallet makes the pallet wavy.
Also, as Frame-0 moves it may leave traces.

demo2 <http://cr.openjdk.java.net/%7Eant/hidpi_pics/demo2.png>

Unstable look of a control. For instance, these two combos are decorated 
differently (and not perfectly).

demo3 <http://cr.openjdk.java.net/%7Eant/hidpi_pics/demo3.png>

Scrolling traces.

demo4 <http://cr.openjdk.java.net/%7Eant/hidpi_pics/demo4.png>

Menu traces.
Colored rendering artifacts.

Additionally, I'm attaching a test source & pics kindly provided by 
Renaud (cc'd) from AndroidStudio. The demo finely shows problems on the 
example of primitive rendering.

Scaling 100% 
<http://cr.openjdk.java.net/%7Eant/hidpi_pics/Scaling-100-percent.png>

Scaling 125% 
<http://cr.openjdk.java.net/%7Eant/hidpi_pics/Scaling-125-percent.png>

Scaling 150% 
<http://cr.openjdk.java.net/%7Eant/hidpi_pics/Scaling-150-percent.png>

It seems like most of the mentioned issues are caused by inaccurate 
rounding performed during the rendering cycle.

So, I'd like to ask you please share your thoughts on it. How serious is 
the problem at all (I guess you're aware of it)? What is solvable on the 
JDK side, and what is not (e.g. demo2 and the Renaud's test case)?
Do you have plans to resolve it by jdk9 GA, or earlier/later? Any 
technical details behind it are very welcome as well.

Thanks in advance,
Anton.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/2d-dev/attachments/20160928/6b2e4477/attachment.html>
-------------- next part --------------
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package testscalingrectangles;

import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.border.LineBorder;

public class MinimalSwingApplication {
    public static void main(String[] args) {
        MinimalSwingApplication app = new MinimalSwingApplication();
        app.buildAndDisplayGui();
    }

    private void buildAndDisplayGui(){
        JFrame frame = new JFrame("Test Frame");
        frame.setPreferredSize(new Dimension(240, 120));
        buildContent(frame);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentMoved(ComponentEvent e) {
                frame.repaint();
            }
        });
    }

    private void buildContent(JFrame aFrame){
        JButton repaintButton = new JButton();
        repaintButton.setText("Repaint");
        repaintButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                aFrame.repaint();
            }
        });

        JLabel scaleFactorLabel = new JLabel();
        JPanel topPanel = new JPanel();
        topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.LINE_AXIS));
        topPanel.add(new JLabel());
        topPanel.add(Box.createHorizontalGlue());
        topPanel.add(scaleFactorLabel);
        topPanel.add(Box.createHorizontalGlue());
        topPanel.add(new JLabel());

        JPanel panel = new JPanel(new BorderLayout());
        panel.add(topPanel, BorderLayout.NORTH);
        panel.add(new MyPanel(scaleFactorLabel), BorderLayout.CENTER);
        panel.add(repaintButton, BorderLayout.SOUTH);
        aFrame.getContentPane().add(panel);
    }

    private class MyPanel extends JPanel {
        private JLabel myLabel;

        public MyPanel(JLabel label) {
            super();
            myLabel = label;

            setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
            setBorder(new LineBorder(Color.BLUE, 2));

            Map<RenderingHints.Key,Object> renderingHints1 = new HashMap<>();
            renderingHints1.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
            add(new MyPaintRectPanel(renderingHints1));

            Map<RenderingHints.Key,Object> renderingHints2 = new HashMap<>();
            renderingHints1.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            add(new MyPaintRectPanel(renderingHints2));
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (g instanceof Graphics2D) {
                Graphics2D g2d = (Graphics2D)g;
                myLabel.setText("Scale=" + (int)(g2d.getTransform().getScaleX() * 100) + "%");
            }

//            int wRect = 5;
//            int hRect = 5;
//            int xRect0 = this.getSize().width / 2 - wRect;
//            int yRect0 = this.getSize().height / 2 - hRect;
//            int xRect1 = xRect0 + wRect + 1;
//            int yRect1 = yRect0 + hRect + 1;
            //myLabel2.setText("X=" + xRect0 + ", Y= " + yRect0);
        }
    }

    private class MyPaintRectPanel extends JPanel {
        private Map<RenderingHints.Key, ?> myRenderingHints;

        public MyPaintRectPanel(Map<RenderingHints.Key, Object> renderingHints) {
            myRenderingHints = renderingHints;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (g instanceof Graphics2D) {
                Graphics2D g2d = (Graphics2D)g;
                for (RenderingHints.Key key : myRenderingHints.keySet()) {
                    g2d.setRenderingHint(key, myRenderingHints.get(key));
                }
                //((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                //g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
                //g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            }

            int wRect = 5;
            int hRect = 5;

            int xRect0 = this.getSize().width / 2 - wRect;
            int yRect0 = this.getSize().height / 2 - hRect;

            int xRect1 = xRect0 + wRect + 1;
            int yRect1 = yRect0 + hRect + 1;

            //myLabel.setText("X=" + xRect0 + ", Y= " + yRect0);

            // Fill the background as RED to illustrate the problem. We are filling
            // a red rectangle of size (0, 0, w, h * 2 + 1)
            // This happens` because of the way rounding works in the default transform.
            // From a API perspective, you would not expect any "red" to remain, since
            // we cover it up
            g.setColor(Color.RED);
            g.fillRect(xRect0, yRect0, wRect * 2 + 1, hRect * 2 + 1);

            // Draw 4 rectangles
            g.setColor(Color.PINK);
            g.fillRect(xRect0, yRect0, wRect, hRect);
            g.fillRect(xRect1, yRect0, wRect, hRect);
            g.fillRect(xRect0, yRect1, wRect, hRect);
            g.fillRect(xRect1, yRect1, wRect, hRect);

            // Draw borders of 4 rectangles
            g.setColor(Color.GREEN);
            g.drawRect(xRect0, yRect0, wRect - 1, hRect - 1);
            g.drawRect(xRect1, yRect0, wRect - 1, hRect - 1);
            g.drawRect(xRect0, yRect1, wRect - 1, hRect - 1);
            g.drawRect(xRect1, yRect1, wRect - 1, hRect - 1);

            // Draw horiz/vert lines in the middle of the 4 rectangles
            g.setColor(Color.BLUE);
            int xMiddle = xRect1 - 1;
            int yMiddle = yRect1 - 1;
            g.drawLine(xRect0, yMiddle, xRect0 + wRect * 2, yMiddle);
            g.drawLine(xMiddle, yRect0, xMiddle, yRect0 + hRect * 2);
        }
    }
}


More information about the 2d-dev mailing list