8178025: HiDPI with non-integer scale factor - SPANs in HTML are rendered overlapping each other
authorpsadhukhan
Thu, 16 Nov 2017 12:24:02 +0530
changeset 47960 ea50ef230511
parent 47841 3b6fc119b32b
child 47961 1f5eeb207a2e
8178025: HiDPI with non-integer scale factor - SPANs in HTML are rendered overlapping each other Reviewed-by: serb, ssadetsky
src/java.desktop/share/classes/java/awt/Component.java
src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java
src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java
src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java
src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolTipUI.java
src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolTipUI.java
src/java.desktop/share/classes/javax/swing/text/GlyphPainter1.java
test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java
test/jdk/javax/swing/JLabel/GetSpanHiDpiBug.java
--- a/src/java.desktop/share/classes/java/awt/Component.java	Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/java/awt/Component.java	Thu Nov 16 12:24:02 2017 +0530
@@ -1135,9 +1135,18 @@
         if (graphicsConfig == gc) {
             return false;
         }
-
+        GraphicsConfiguration oldConfig = graphicsConfig;
         graphicsConfig = gc;
 
+        /*
+         * If component is moved from one screen to another sceeen
+         * graphicsConfiguration property is fired to enable the component
+         * to recalculate any rendering data, if needed
+         */
+        if (oldConfig != null && gc != null) {
+            firePropertyChange("graphicsConfiguration", oldConfig, gc);
+        }
+
         ComponentPeer peer = this.peer;
         if (peer != null) {
             return peer.updateGraphicsData(gc);
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java	Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java	Thu Nov 16 12:24:02 2017 +0530
@@ -75,7 +75,8 @@
             checkOpacity((AbstractButton) e.getSource() );
         }
         else if(prop == AbstractButton.TEXT_CHANGED_PROPERTY ||
-                "font" == prop || "foreground" == prop) {
+                "font" == prop || "foreground" == prop ||
+                "ancestor" == prop || "graphicsConfiguration" == prop) {
             AbstractButton b = (AbstractButton) e.getSource();
             BasicHTML.updateRenderer(b, b.getText());
         }
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java	Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java	Thu Nov 16 12:24:02 2017 +0530
@@ -472,7 +472,8 @@
 
     public void propertyChange(PropertyChangeEvent e) {
         String name = e.getPropertyName();
-        if (name == "text" || "font" == name || "foreground" == name) {
+        if (name == "text" || "font" == name || "foreground" == name ||
+            "ancestor" == name || "graphicsConfiguration" == name) {
             // remove the old html view client property if one
             // existed, and install a new one if the text installed
             // into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java	Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java	Thu Nov 16 12:24:02 2017 +0530
@@ -1106,7 +1106,8 @@
                 name == "accelerator") {
                 updateAcceleratorBinding();
             } else if (name == "text" || "font" == name ||
-                       "foreground" == name) {
+                       "foreground" == name ||
+                       "ancestor" == name || "graphicsConfiguration" == name) {
                 // remove the old html view client property if one
                 // existed, and install a new one if the text installed
                 // into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolTipUI.java	Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToolTipUI.java	Thu Nov 16 12:24:02 2017 +0530
@@ -261,7 +261,8 @@
         public void propertyChange(PropertyChangeEvent e) {
             String name = e.getPropertyName();
             if (name.equals("tiptext") || "font".equals(name) ||
-                "foreground".equals(name)) {
+                "foreground".equals(name) ||
+                "ancestor" == name || "graphicsConfiguration" == name) {
                 // remove the old html view client property if one
                 // existed, and install a new one if the text installed
                 // into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolTipUI.java	Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthToolTipUI.java	Thu Nov 16 12:24:02 2017 +0530
@@ -227,7 +227,8 @@
         }
         String name = e.getPropertyName();
         if (name.equals("tiptext") || "font".equals(name) ||
-                "foreground".equals(name)) {
+                "foreground".equals(name) ||
+                "ancestor" == name || "graphicsConfiguration" == name) {
             // remove the old html view client property if one
             // existed, and install a new one if the text installed
             // into the JLabel is html source.
--- a/src/java.desktop/share/classes/javax/swing/text/GlyphPainter1.java	Wed Nov 15 09:31:17 2017 -0800
+++ b/src/java.desktop/share/classes/javax/swing/text/GlyphPainter1.java	Thu Nov 16 12:24:02 2017 +0530
@@ -59,7 +59,8 @@
         sync(v);
         Segment text = v.getText(p0, p1);
         int[] justificationData = getJustificationData(v);
-        int width = Utilities.getTabbedTextWidth(v, text, metrics, (int) x, e, p0,
+
+        float width = Utilities.getTabbedTextWidth(v, text, metrics, x, e, p0,
                                                  justificationData);
         SegmentCache.releaseSharedSegment(text);
         return width;
@@ -222,10 +223,15 @@
     @SuppressWarnings("deprecation")
     void sync(GlyphView v) {
         Font f = v.getFont();
-        if ((metrics == null) || (! f.equals(metrics.getFont()))) {
+        FontMetrics fm = null;
+        Container c = v.getContainer();
+        if (c != null) {
+            fm = c.getFontMetrics(f);
+        }
+        if ((metrics == null) || (! f.equals(metrics.getFont()))
+                || (! metrics.equals(fm))) {
             // fetch a new FontMetrics
-            Container c = v.getContainer();
-            metrics = (c != null) ? c.getFontMetrics(f) :
+            metrics = (c != null) ? fm :
                 Toolkit.getDefaultToolkit().getFontMetrics(f);
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java	Thu Nov 16 12:24:02 2017 +0530
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8178025
+ * @summary  Verifies if graphicsConfiguration property notification is sent
+ *           when frame is moved from one screen to another in multiscreen
+ *           environment.
+ * @run main TestMultiScreenGConfigNotify
+ */
+
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+
+public class TestMultiScreenGConfigNotify {
+
+    static JFrame f;
+    static String propName[];
+    static int propCount = 0;
+    static boolean result = false;
+    public static void main(String[] args) throws Exception {
+
+        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        GraphicsDevice[] gds = ge.getScreenDevices();
+        if (gds.length < 2) {
+            return;
+        }
+        GraphicsConfiguration gc = gds[0].getDefaultConfiguration();
+        GraphicsConfiguration gc2 = gds[1].getDefaultConfiguration();
+        propName = new String[10];
+        SwingUtilities.invokeAndWait(() -> {
+            f = new JFrame();
+            f.setSize(300, 300);
+            f.setBounds(gc.getBounds().x, gc.getBounds().y, f.getWidth(), f.getHeight());
+            f.setVisible(true);
+
+            f.addPropertyChangeListener((PropertyChangeEvent evt) -> {
+                String name = evt.getPropertyName();
+                System.out.println("propertyChange " + name);
+                propName[propCount] = name;
+                propCount++;
+            });
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException ex) {
+            }
+            f.setBounds(gc2.getBounds().x, gc2.getBounds().y,
+                        f.getWidth(), f.getHeight());
+        });
+
+        Thread.sleep(1000);
+        for(int i = 0; i < propCount; i++) {
+            if (propName[i].equals("graphicsConfiguration")) {
+                result = true;
+            }
+        }
+        SwingUtilities.invokeAndWait(() ->  f.dispose());
+        if(!result) {
+            throw new RuntimeException("graphicsConfiguration notification not sent");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/swing/JLabel/GetSpanHiDpiBug.java	Thu Nov 16 12:24:02 2017 +0530
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8178025
+ * @summary  Verifies if SPANs in HTML text are rendered properly in hidpi
+ * @run main/manual/othervm -Dsun.java2d.uiScale=2.25 GetSpanHiDpiBug
+ */
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.beans.PropertyChangeSupport;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+
+public class GetSpanHiDpiBug {
+    public static void main(String[] args) throws Exception {
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        SpanTest test = new SpanTest(latch);
+        Thread T1 = new Thread(test);
+        T1.start();
+
+        // wait for latch to complete
+        boolean ret = false;
+        try {
+            ret = latch.await(3000, TimeUnit.SECONDS);
+        } catch (InterruptedException ie) {
+            throw ie;
+        }
+        if (!ret) {
+            test.dispose();
+            throw new RuntimeException(" User has not executed the test");
+        }
+        if (test.testResult == false) {
+            throw new RuntimeException("Some characters overlap");
+        }
+    }
+}
+
+class SpanTest implements Runnable {
+    static JFrame f;
+    static JDialog dialog;
+    public boolean testResult = false;
+    private final CountDownLatch latch;
+
+    public SpanTest(CountDownLatch latch) throws Exception {
+        this.latch = latch;
+    }
+
+    @Override
+    public void run() {
+        try {
+            createUI();
+            spanTest();
+        } catch (Exception ex) {
+            dispose();
+            latch.countDown();
+            throw new RuntimeException("createUI Failed: " + ex.getMessage());
+        }
+    }
+
+    public void dispose() {
+        if (dialog != null) {
+            dialog.dispose();
+        }
+        if (f != null) {
+            f.dispose();
+        }
+    }
+
+    private static void spanTest() throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                JLabel label =
+                    new JLabel("<html><span>A few words to get started "
+                    + "before the bug</span><span>overlapping text</span></html>");
+                f = new JFrame("");
+                f.getContentPane().add(label, BorderLayout.CENTER);
+                f.setSize(500,500);
+                f.setVisible(true);
+            }
+        });
+    }
+
+    private final void createUI() throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                String description
+                        = " INSTRUCTIONS:\n"
+                        + " A string will be shown.\n "
+                        + " Press Pass if there is no overlap of characters\n"
+                        + " else press Fail.";
+
+                dialog = new JDialog();
+                dialog.setTitle("textselectionTest");
+                JTextArea textArea = new JTextArea(description);
+                textArea.setEditable(false);
+                final JButton passButton = new JButton("PASS");
+                passButton.addActionListener((e) -> {
+                    testResult = true;
+                    dispose();
+                    latch.countDown();
+                });
+                final JButton failButton = new JButton("FAIL");
+                failButton.addActionListener((e) -> {
+                    testResult = false;
+                    dispose();
+                    latch.countDown();
+                });
+                JPanel mainPanel = new JPanel(new BorderLayout());
+                mainPanel.add(textArea, BorderLayout.CENTER);
+                JPanel buttonPanel = new JPanel(new FlowLayout());
+                buttonPanel.add(passButton);
+                buttonPanel.add(failButton);
+                mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+                dialog.add(mainPanel);
+                dialog.pack();
+                dialog.setVisible(true);
+            }
+        });
+    }
+}