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

Uneven text weight for different font sizes with Fractional Metrics and gray AA

    XMLWordPrintable

Details

    • 2d
    • 13
    • x86_64
    • windows_10

    Description

      A DESCRIPTION OF THE PROBLEM :
      In OpenJDK 12 and earlier, the text weight is increased smoothly with the font size in VALUE_FRACTIONALMETRICS_ON mode and any text antialiasing mode on. In OpenJDK 13 the weight is increased sharply with 'gray' antialiasing. At least the sharp difference can be seen between 17 and 18 pt. The same issue can be seen using affine transform scaling. So we cannot implement smoothly resized string on the screen.

      The issue only comes with 'gray' text antialiasing (VALUE_TEXT_ANTIALIAS_ON). With HRGB subpixel antialising there is no issue. But if we apply rotation or shear transform the issue is visible in HRGB mode also.

      There is no such issue in 11.0.5 and 12.0.2, so it is a regression.

      BTW, shear transform works incorrectly with fractional metrics on, but it's not critical for us...

      REGRESSION : Last worked in version 12


      ---------- BEGIN SOURCE ----------
      package texttest;

      import java.awt.BorderLayout;
      import java.awt.Color;
      import java.awt.Dimension;
      import java.awt.Font;
      import java.awt.Graphics;
      import java.awt.Graphics2D;
      import java.awt.GraphicsEnvironment;
      import java.awt.HeadlessException;
      import java.awt.Point;
      import java.awt.RenderingHints;
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import javax.swing.Box;
      import javax.swing.JCheckBox;
      import javax.swing.JComboBox;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JPanel;
      import javax.swing.JSeparator;
      import javax.swing.JToggleButton;
      import javax.swing.SwingUtilities;

      /**
       *
       * @author Alexander
       */
      public class MPTextTest extends JFrame
      {
        private JCheckBox cboxScale = new JCheckBox("Scale 90%");
        private JCheckBox cboxRotate = new JCheckBox("Rotate 10\u00b0");
        private JCheckBox cboxShare = new JCheckBox("Oblique 18\u00b0");
        private JCheckBox cboxFractionalMetrics = new JCheckBox("Fractional Metrics");
        private JComboBox cboxTextAntialiasing = new JComboBox();
        private JComboBox cboxFont = new JComboBox();
        private JToggleButton btnBold = new JToggleButton("B");
        private JToggleButton btnItalic = new JToggleButton("I");

        public MPTextTest() throws HeadlessException
        {
          super("MP TextTest");
          setDefaultCloseOperation(EXIT_ON_CLOSE);

          cboxFractionalMetrics.setToolTipText("<html>The <tt>FRACTIONALMETRICS</tt> hint controls whether the positioning of<br>"
                  + "individual character glyphs takes into account the<br>"
                  + "sub-pixel accuracy of the scaled character advances of<br>"
                  + "the font or whether such advance vectors are rounded<br>"
                  + "to an integer number of whole device pixels.");

          cboxFractionalMetrics.setSelected(true);
          cboxTextAntialiasing.addItem("On (Gray)");
          cboxTextAntialiasing.addItem("Off");
          cboxTextAntialiasing.addItem("Default");
          cboxTextAntialiasing.addItem("Gasp");
          cboxTextAntialiasing.addItem("LCD HBGR");
          cboxTextAntialiasing.addItem("LCD HRGB");
          cboxTextAntialiasing.addItem("LCD VBGR");
          cboxTextAntialiasing.addItem("LCD VRGB");
          cboxTextAntialiasing.setMaximumSize(cboxTextAntialiasing.getMinimumSize());

          for (String fontName : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames())
            cboxFont.addItem(fontName);
          cboxFont.setMaximumSize(cboxFont.getMinimumSize());

          cboxFont.setSelectedItem("Times New Roman");

          final JPanel textPanel = new JPanel()
          {
            @Override
            protected void paintComponent(Graphics g)
            {
              super.paintComponent(g);
              Graphics2D gr = (Graphics2D) g.create();

              Dimension size = getSize();

              gr.setColor(Color.white);
              gr.fillRect(0, 0, size.width, size.height);
              String text = "The quick brown fox jumps over the lazy dog 1234567890";

              gr.setColor(Color.black);

              Object aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
              switch (cboxTextAntialiasing.getSelectedIndex())
              {
                case 0:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
                  break;
                case 1:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
                  break;
                case 2:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT;
                  break;
                case 3:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_GASP;
                  break;
                case 4:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR;
                  break;
                case 5:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB;
                  break;
                case 6:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR;
                  break;
                case 7:
                  aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB;
                  break;
              }
              gr.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, aaHint);
              gr.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, cboxFractionalMetrics.isSelected() ? RenderingHints.VALUE_FRACTIONALMETRICS_ON : RenderingHints.VALUE_FRACTIONALMETRICS_OFF);

              int pos = 7;
              for (int fontSize = 4; fontSize <= 25; fontSize++)
              {
                Graphics2D gr1 = (Graphics2D) gr.create();
                gr1.translate(5, pos);
                if (cboxScale.isSelected())
                  gr1.scale(0.9, 0.9);
                if (cboxRotate.isSelected())
                  gr1.rotate(Math.PI / 18.0);
                if (cboxShare.isSelected())
                  gr1.shear(-Math.atan(18.0 / 180.0 * Math.PI), 0);

                int style = Font.PLAIN;
                if (btnBold.isSelected())
                  style = style | Font.BOLD;
                if (btnItalic.isSelected())
                  style = style | Font.ITALIC;

                gr1.setFont(new Font(cboxFont.getSelectedItem().toString(), style, fontSize));
                gr1.drawString(fontSize + " " + text, 0, 0);
                gr1.dispose();
                pos += fontSize;
              }

              gr.dispose();
            }
          };

          textPanel.setPreferredSize(new Dimension(650, 320));

          Box box = Box.createHorizontalBox();
          box.add(Box.createHorizontalStrut(5));
          box.add(new JLabel("Text Antialiasing:"));
          box.add(Box.createHorizontalStrut(5));
          box.add(cboxTextAntialiasing);
          box.add(Box.createHorizontalStrut(5));
          box.add(cboxFractionalMetrics);
          box.add(Box.createHorizontalGlue());
          box.add(Box.createHorizontalStrut(15));
          box.add(cboxFont);
          box.add(Box.createHorizontalStrut(5));
          box.add(btnBold);
          box.add(Box.createHorizontalStrut(5));
          box.add(btnItalic);
          box.add(Box.createHorizontalStrut(5));

          Box boxT = Box.createHorizontalBox();
          boxT.add(Box.createHorizontalStrut(5));
          boxT.add(new JLabel("Affine Transform:"));
          boxT.add(Box.createHorizontalStrut(5));
          boxT.add(cboxScale);
          boxT.add(Box.createHorizontalStrut(5));
          boxT.add(cboxRotate);
          boxT.add(Box.createHorizontalStrut(5));
          boxT.add(cboxShare);
          boxT.add(Box.createHorizontalStrut(5));
          boxT.add(Box.createHorizontalGlue());

          Box box3 = Box.createHorizontalBox();
          box3.add(Box.createHorizontalStrut(5));
          box3.add(new JLabel("Java VM version: " + System.getProperty("java.vm.vendor") + " "
                  + System.getProperty("java.vm.name") + " " + System.getProperty("java.version")
                  + ", on " + System.getProperty("os.name") + " "
                  + System.getProperty("os.arch") + " " + System.getProperty("os.version")));
          box3.add(Box.createHorizontalStrut(5));
          box3.add(Box.createHorizontalGlue());

          Box box2 = Box.createVerticalBox();
          box2.add(Box.createVerticalStrut(5));
          box2.add(box);
          box2.add(Box.createVerticalStrut(5));
          box2.add(boxT);
          box2.add(Box.createVerticalStrut(5));
          box2.add(box3);
          box2.add(Box.createVerticalStrut(3));
          box2.add(new JSeparator(JSeparator.HORIZONTAL));
          box2.add(Box.createVerticalStrut(3));
          box2.add(box3);
          box2.add(Box.createVerticalStrut(5));

          ActionListener listener = new ActionListener()
          {
            public void actionPerformed(ActionEvent e)
            {
              textPanel.repaint();
            }
          };
          cboxScale.addActionListener(listener);
          cboxRotate.addActionListener(listener);
          cboxShare.addActionListener(listener);
          cboxFractionalMetrics.addActionListener(listener);
          cboxTextAntialiasing.addActionListener(listener);
          cboxFont.addActionListener(listener);
          btnBold.addActionListener(listener);
          btnItalic.addActionListener(listener);

          getContentPane().setLayout(new BorderLayout());
          getContentPane().add(textPanel, BorderLayout.CENTER);
          getContentPane().add(box2, BorderLayout.SOUTH);
          pack();
          setSize(getPreferredSize());

          Point screenCenter = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
          setLocation(screenCenter.x - getSize().width / 2, screenCenter.y - getSize().height / 2);
        }

        public static void main(String[] args)
        {
          SwingUtilities.invokeLater(new Runnable()
          {
            @Override
            public void run()
            {
              JFrame frame = new MPTextTest();
              frame.setVisible(true);
            }
          });
        }
      }

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

      FREQUENCY : always


      Attachments

        Issue Links

          Activity

            People

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

              Dates

                Created:
                Updated:
                Resolved: