8168992: Add floating point implementation for new BasicGraphicsUtils text related methods use floating point API
authoralexsch
Mon, 07 Nov 2016 11:22:53 +0300
changeset 42200 0ab62056c933
parent 42199 8c0e8e13d4d8
child 42201 cdf51b0d0361
8168992: Add floating point implementation for new BasicGraphicsUtils text related methods use floating point API Reviewed-by: serb, ssadetsky
jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicGraphicsUtils.java
jdk/src/java.desktop/share/classes/javax/swing/text/Utilities.java
jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java
jdk/test/javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicGraphicsUtils.java	Mon Nov 07 10:36:52 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicGraphicsUtils.java	Mon Nov 07 11:22:53 2016 +0300
@@ -403,7 +403,7 @@
      */
     public static void drawString(JComponent c, Graphics2D g, String string,
                                   float x, float y) {
-        SwingUtilities2.drawString(c, g, string, (int) x, (int) y);
+        SwingUtilities2.drawString(c, g, string, x, y, true);
     }
 
     /**
@@ -439,7 +439,7 @@
     public static void drawStringUnderlineCharAt(JComponent c, Graphics2D g,
             String string, int underlinedIndex, float x, float y) {
         SwingUtilities2.drawStringUnderlineCharAt(c, g, string, underlinedIndex,
-                                                  (int) x, (int) y);
+                                                  x, y, true);
     }
 
     /**
@@ -482,6 +482,6 @@
      * @since 9
      */
     public static float getStringWidth(JComponent c, FontMetrics fm, String string) {
-        return SwingUtilities2.stringWidth(c, fm, string);
+        return SwingUtilities2.stringWidth(c, fm, string, true);
     }
 }
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/Utilities.java	Mon Nov 07 10:36:52 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/Utilities.java	Mon Nov 07 11:22:53 2016 +0300
@@ -107,7 +107,7 @@
                                              TabExpander e,
                                              int startOffset)
     {
-        return drawTabbedText(s, (int) x, (int) y, (Graphics) g, e, startOffset);
+        return drawTabbedText(null, s, x, y, g, e, startOffset, null, true);
     }
 
     /**
--- a/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java	Mon Nov 07 10:36:52 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java	Mon Nov 07 11:22:53 2016 +0300
@@ -318,7 +318,21 @@
      * @param fm FontMetrics used to measure the String width
      * @param string String to get the width of
      */
-    public static int stringWidth(JComponent c, FontMetrics fm, String string){
+    public static int stringWidth(JComponent c, FontMetrics fm, String string) {
+        return (int) stringWidth(c, fm, string, false);
+    }
+
+    /**
+     * Returns the width of the passed in String.
+     * If the passed String is {@code null}, returns zero.
+     *
+     * @param c JComponent that will display the string, may be null
+     * @param fm FontMetrics used to measure the String width
+     * @param string String to get the width of
+     * @param useFPAPI use floating point API
+     */
+    public static float stringWidth(JComponent c, FontMetrics fm, String string,
+            boolean useFPAPI){
         if (string == null || string.equals("")) {
             return 0;
         }
@@ -333,9 +347,9 @@
         if (needsTextLayout) {
             TextLayout layout = createTextLayout(c, string,
                                     fm.getFont(), fm.getFontRenderContext());
-            return (int) layout.getAdvance();
+            return layout.getAdvance();
         } else {
-            return fm.stringWidth(string);
+            return getFontStringWidth(string, fm, useFPAPI);
         }
     }
 
@@ -426,6 +440,21 @@
      */
     public static void drawString(JComponent c, Graphics g, String text,
                                   int x, int y) {
+        drawString(c, g, text, x, y, false);
+    }
+
+    /**
+     * Draws the string at the specified location.
+     *
+     * @param c JComponent that will display the string, may be null
+     * @param g Graphics to draw the text to
+     * @param text String to display
+     * @param x X coordinate to draw the text at
+     * @param y Y coordinate to draw the text at
+     * @param useFPAPI use floating point API
+     */
+    public static void drawString(JComponent c, Graphics g, String text,
+                                  float x, float y, boolean useFPAPI) {
         // c may be null
 
         // All non-editable widgets that draw strings call into this
@@ -509,7 +538,7 @@
                                                     g2.getFontRenderContext());
                     layout.draw(g2, x, y);
                 } else {
-                    g.drawString(text, x, y);
+                    g2.drawString(text, x, y);
                 }
 
                 if (oldAAValue != null) {
@@ -530,7 +559,7 @@
             }
         }
 
-        g.drawString(text, x, y);
+        g.drawString(text, (int) x, (int) y);
     }
 
     /**
@@ -544,17 +573,36 @@
      * @param x X coordinate to draw the text at
      * @param y Y coordinate to draw the text at
      */
+
     public static void drawStringUnderlineCharAt(JComponent c,Graphics g,
-                           String text, int underlinedIndex, int x,int y) {
+                           String text, int underlinedIndex, int x, int y) {
+        drawStringUnderlineCharAt(c, g, text, underlinedIndex, x, y, false);
+    }
+    /**
+     * Draws the string at the specified location underlining the specified
+     * character.
+     *
+     * @param c JComponent that will display the string, may be null
+     * @param g Graphics to draw the text to
+     * @param text String to display
+     * @param underlinedIndex Index of a character in the string to underline
+     * @param x X coordinate to draw the text at
+     * @param y Y coordinate to draw the text at
+     * @param useFPAPI use floating point API
+     */
+    public static void drawStringUnderlineCharAt(JComponent c, Graphics g,
+                                                 String text, int underlinedIndex,
+                                                 float x, float y,
+                                                 boolean useFPAPI) {
         if (text == null || text.length() <= 0) {
             return;
         }
-        SwingUtilities2.drawString(c, g, text, x, y);
+        SwingUtilities2.drawString(c, g, text, x, y, useFPAPI);
         int textLength = text.length();
         if (underlinedIndex >= 0 && underlinedIndex < textLength ) {
-            int underlineRectY = y;
+            float underlineRectY = y;
             int underlineRectHeight = 1;
-            int underlineRectX = 0;
+            float underlineRectX = 0;
             int underlineRectWidth = 0;
             boolean isPrinting = isPrinting(g);
             boolean needsTextLayout = isPrinting;
@@ -594,7 +642,7 @@
                     underlineRectWidth = rect.width;
                 }
             }
-            g.fillRect(underlineRectX, underlineRectY + 1,
+            g.fillRect((int) underlineRectX, (int) underlineRectY + 1,
                        underlineRectWidth, underlineRectHeight);
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java	Mon Nov 07 11:22:53 2016 +0300
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.font.FontRenderContext;
+import java.awt.font.NumericShaper;
+import java.awt.font.TextAttribute;
+import java.awt.font.TextLayout;
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.basic.BasicGraphicsUtils;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+
+/**
+ * @test
+ * @bug 8132119 8168992
+ * @author Alexandr Scherbatiy
+ * @summary Provide public API for text related methods in SwingBasicGraphicsUtils2
+ */
+public class bug8132119 {
+
+    private static final int WIDTH = 50;
+    private static final int HEIGHT = 50;
+    private static final Color DRAW_COLOR = Color.RED;
+    private static final Color BACKGROUND_COLOR = Color.GREEN;
+    private static final NumericShaper NUMERIC_SHAPER = NumericShaper.getShaper(
+            NumericShaper.ARABIC);
+
+    public static void main(String[] args) throws Exception {
+        SwingUtilities.invokeAndWait(bug8132119::testStringMethods);
+    }
+
+    private static void testStringMethods() {
+        setMetalLAF();
+        testStringWidth();
+        testStringClip();
+        testDrawEmptyString();
+        testDrawString(false);
+        testDrawString(true);
+        checkNullArguments();
+    }
+
+    private static void testStringWidth() {
+
+        String str = "12345678910\u036F";
+        JComponent comp = createComponent(str);
+        Font font = comp.getFont();
+        FontMetrics fontMetrics = comp.getFontMetrics(font);
+        float stringWidth = BasicGraphicsUtils.getStringWidth(comp, fontMetrics, str);
+
+        if (stringWidth == fontMetrics.stringWidth(str)) {
+            throw new RuntimeException("Numeric shaper is not used!");
+        }
+
+        if (stringWidth != getLayoutWidth(str, font, NUMERIC_SHAPER)) {
+            throw new RuntimeException("Wrong text width!");
+        }
+    }
+
+    private static void testStringClip() {
+
+        String str = "1234567890";
+        JComponent comp = createComponent(str);
+        FontMetrics fontMetrics = comp.getFontMetrics(comp.getFont());
+
+        int width = (int) BasicGraphicsUtils.getStringWidth(comp, fontMetrics, str);
+
+        String clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, width);
+        checkClippedString(str, clip, str);
+
+        clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, width + 1);
+        checkClippedString(str, clip, str);
+
+        clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, -1);
+        checkClippedString(str, clip, "...");
+
+        clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics, str, 0);
+        checkClippedString(str, clip, "...");
+
+        clip = BasicGraphicsUtils.getClippedString(comp, fontMetrics,
+                str, width - width / str.length());
+        int endIndex = str.length() - 3;
+        checkClippedString(str, clip, str.substring(0, endIndex) + "...");
+    }
+
+    private static void checkClippedString(String str, String res, String golden) {
+        if (!golden.equals(res)) {
+            throw new RuntimeException(String.format("The string '%s' is not "
+                    + "properly clipped. The result is '%s' instead of '%s'",
+                    str, res, golden));
+        }
+    }
+
+    private static void testDrawEmptyString() {
+        JLabel label = new JLabel();
+        BufferedImage buffImage = createBufferedImage(50, 50);
+        Graphics2D g2 = buffImage.createGraphics();
+        g2.setColor(DRAW_COLOR);
+        BasicGraphicsUtils.drawString(null, g2, null, 0, 0);
+        BasicGraphicsUtils.drawString(label, g2, null, 0, 0);
+        BasicGraphicsUtils.drawString(null, g2, "", 0, 0);
+        BasicGraphicsUtils.drawString(label, g2, "", 0, 0);
+        BasicGraphicsUtils.drawStringUnderlineCharAt(null, g2, null, 3, 0, 0);
+        BasicGraphicsUtils.drawStringUnderlineCharAt(label, g2, null, 3, 0, 0);
+        BasicGraphicsUtils.drawStringUnderlineCharAt(null, g2, "", 3, 0, 0);
+        BasicGraphicsUtils.drawStringUnderlineCharAt(label, g2, "", 3, 0, 0);
+        g2.dispose();
+        checkImageIsEmpty(buffImage);
+    }
+
+    private static void testDrawString(boolean underlined) {
+        String str = "AOB";
+        JComponent comp = createComponent(str);
+
+        BufferedImage buffImage = createBufferedImage(WIDTH, HEIGHT);
+        Graphics2D g2 = buffImage.createGraphics();
+
+        g2.setColor(DRAW_COLOR);
+        g2.setFont(comp.getFont());
+
+        FontMetrics fontMetrices = comp.getFontMetrics(comp.getFont());
+        float width = BasicGraphicsUtils.getStringWidth(comp, fontMetrices, str);
+        float x = (WIDTH - width) / 2;
+        int y = 3 * HEIGHT / 4;
+
+        if (underlined) {
+            BasicGraphicsUtils.drawStringUnderlineCharAt(comp, g2, str, 1, x, y);
+        } else {
+            BasicGraphicsUtils.drawString(comp, g2, str, x, y);
+        }
+        g2.dispose();
+
+        float xx = (WIDTH - width / 8) / 2;
+        checkImageContainsSymbol(buffImage, (int) xx, underlined ? 3 : 2);
+    }
+
+    private static void checkNullArguments() {
+
+        Graphics2D g = null;
+        try {
+            String text = "Test";
+            JComponent component = new JLabel(text);
+            BufferedImage img = createBufferedImage(100, 100);
+            g = img.createGraphics();
+            checkNullArguments(component, g, text);
+        } finally {
+            g.dispose();
+        }
+    }
+
+    private static void checkNullArguments(JComponent comp, Graphics2D g,
+            String text) {
+
+        checkNullArgumentsDrawString(comp, g, text);
+        checkNullArgumentsDrawStringUnderlineCharAt(comp, g, text);
+        checkNullArgumentsGetClippedString(comp, text);
+        checkNullArgumentsGetStringWidth(comp, text);
+    }
+
+    private static void checkNullArgumentsDrawString(JComponent comp, Graphics2D g,
+            String text) {
+
+        float x = 50;
+        float y = 50;
+        BasicGraphicsUtils.drawString(null, g, text, x, y);
+        BasicGraphicsUtils.drawString(comp, g, null, x, y);
+
+        try {
+            BasicGraphicsUtils.drawString(comp, null, text, x, y);
+        } catch (NullPointerException e) {
+            return;
+        }
+
+        throw new RuntimeException("NPE is not thrown");
+    }
+
+    private static void checkNullArgumentsDrawStringUnderlineCharAt(
+            JComponent comp, Graphics2D g, String text) {
+
+        int x = 50;
+        int y = 50;
+        BasicGraphicsUtils.drawStringUnderlineCharAt(null, g, text, 1, x, y);
+        BasicGraphicsUtils.drawStringUnderlineCharAt(comp, g, null, 1, x, y);
+
+        try {
+            BasicGraphicsUtils.drawStringUnderlineCharAt(comp, null, text, 1, x, y);
+        } catch (NullPointerException e) {
+            return;
+        }
+
+        throw new RuntimeException("NPE is not thrown");
+    }
+
+    private static void checkNullArgumentsGetClippedString(
+            JComponent comp, String text) {
+
+        FontMetrics fontMetrics = comp.getFontMetrics(comp.getFont());
+
+        BasicGraphicsUtils.getClippedString(null, fontMetrics, text, 1);
+        String result = BasicGraphicsUtils.getClippedString(comp, fontMetrics, null, 1);
+        if (!"".equals(result)) {
+            throw new RuntimeException("Empty string is not returned!");
+        }
+
+        try {
+            BasicGraphicsUtils.getClippedString(comp, null, text, 1);
+        } catch (NullPointerException e) {
+            return;
+        }
+
+        throw new RuntimeException("NPE is not thrown");
+    }
+
+    private static void checkNullArgumentsGetStringWidth(JComponent comp,
+            String text) {
+
+        FontMetrics fontMetrics = comp.getFontMetrics(comp.getFont());
+        BasicGraphicsUtils.getStringWidth(null, fontMetrics, text);
+        float result = BasicGraphicsUtils.getStringWidth(comp, fontMetrics, null);
+
+        if (result != 0) {
+            throw new RuntimeException("The string length is not 0");
+        }
+
+        try {
+            BasicGraphicsUtils.getStringWidth(comp, null, text);
+        } catch (NullPointerException e) {
+            return;
+        }
+
+        throw new RuntimeException("NPE is not thrown");
+    }
+
+    private static void setMetalLAF() {
+        try {
+            UIManager.setLookAndFeel(new MetalLookAndFeel());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static JComponent createComponent(String str) {
+        JComponent comp = new JLabel(str);
+        comp.setSize(WIDTH, HEIGHT);
+        comp.putClientProperty(TextAttribute.NUMERIC_SHAPING, NUMERIC_SHAPER);
+        comp.setFont(getFont());
+        return comp;
+    }
+
+    private static Font getFont() {
+        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        String[] fontNames = ge.getAvailableFontFamilyNames();
+        String fontName = fontNames[0];
+        for (String name : fontNames) {
+            if ("Arial".equals(name)) {
+                fontName = name;
+                break;
+            }
+        }
+        return new Font(fontName, Font.PLAIN, 28);
+    }
+
+    private static float getLayoutWidth(String text, Font font, NumericShaper shaper) {
+        HashMap map = new HashMap();
+        map.put(TextAttribute.FONT, font);
+        map.put(TextAttribute.NUMERIC_SHAPING, shaper);
+        FontRenderContext frc = new FontRenderContext(null, false, false);
+        TextLayout layout = new TextLayout(text, map, frc);
+        return layout.getAdvance();
+    }
+
+    private static void checkImageIsEmpty(BufferedImage buffImage) {
+        int background = BACKGROUND_COLOR.getRGB();
+
+        for (int i = 0; i < buffImage.getWidth(); i++) {
+            for (int j = 0; j < buffImage.getHeight(); j++) {
+                if (background != buffImage.getRGB(i, j)) {
+                    throw new RuntimeException("Image is not empty!");
+                }
+            }
+        }
+    }
+
+    private static void checkImageContainsSymbol(BufferedImage buffImage,
+            int x, int intersections) {
+        int background = BACKGROUND_COLOR.getRGB();
+        boolean isBackground = true;
+        int backgroundChangesCount = 0;
+
+        for (int y = 0; y < buffImage.getHeight(); y++) {
+            if (!(isBackground ^ (background != buffImage.getRGB(x, y)))) {
+                isBackground = !isBackground;
+                backgroundChangesCount++;
+            }
+        }
+        if (backgroundChangesCount != intersections * 2) {
+            throw new RuntimeException("String is not properly drawn!");
+        }
+    }
+
+    private static BufferedImage createBufferedImage(int width, int height) {
+        BufferedImage bufffImage = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_RGB);
+
+        Graphics2D g = bufffImage.createGraphics();
+        g.setColor(BACKGROUND_COLOR);
+        g.fillRect(0, 0, width, height);
+        g.dispose();
+        return bufffImage;
+    }
+}