JDK-8233598 : Uneven text weight for different font sizes with Fractional Metrics and gray AA
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 13,14
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2019-10-30
  • Updated: 2020-05-19
  • Resolved: 2020-05-19
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.
Other
tbdResolved
Related Reports
Duplicate :  
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



Comments
For the record the freetype engine in the various releases since 11 is as follows. https://bugs.openjdk.java.net/browse/JDK-8203367 Upgrade to Freetype 2.9.1 JDK 11 b18 https://bugs.openjdk.java.net/browse/JDK-8222362 Upgrade to Freetype 2.10.0 JDK 13 b21 backported to JDK 11.0.5 https://bugs.openjdk.java.net/browse/JDK-8227324 : Upgrade to Freetype 2.10.1 JDK 14 b26 backported to 11.0.7 So JDK 11 uses freetype 2.9.1 or later. All versions since 2.7 default to a newer TT hinting engine. This did give more linear scaling but a lot of people did not like the rendering So JDK 13 b25 implemented https://bugs.openjdk.java.net/browse/JDK-8217731 which changed this, and introduced what the submitter reports here. I can see what he sees from b25 onwards. It was essentially by chance (or design of the author) that the newer hinting engine did what the submitter wanted in JDK 11 and JDK 12. JDK 14 however then implemented https://bugs.openjdk.java.net/browse/JDK-8224109 which means so long as you specify FM along with AA, you'll get that linear scaling glyph images because hinting is disabled again. So 8224109 is the best candidate here.
19-05-2020

Attached screenshot as shared by the submitter. "These screenshots were created by the code provided by me in the ticket."
16-05-2020

@the reporter : Actually I said 8214481 could not be what you are complaining about, since you see a problem with JDK 13 and that did not have this fix. Now it sounds like you think 8214481 is actually the solution you are looking for. but then the statements about this being "OK" in 11.0.5 and 12.0.2 have to be completely wrong, since the issue was definitely there in 11 and 12. So I don't know what to believe. I am reluctantly closing this as "not reproducible" which is not my favourite resolution.
15-05-2020

Additional Information: We have originally tested JDK 13 with Times New Roman font (as in code above), but the issue was actual with other fonts; system was Windows 10 x64. Now I have just checked AdopdOpenJDK 14.0.1 (Win 10) and I confirm that the issue (as we understand it for our product) is fixed in 14, I suppose by https://bugs.openjdk.java.net/browse/JDK-8214481 as you stated. So we will just update from JDK 12 to 14, skipping 13 in production, thank you. I also checked shear transform issue in JDK 15-ea+22 and confirm that it is fixed (https://bugs.openjdk.java.net/browse/JDK-8224109). There is also no problems with the primary issue in 15, just checked. So this ticket can be closed with 'fixed in 14/15' resolution I think, thank you.
15-05-2020

> BTW, shear transform works incorrectly with fractional metrics on, but it's not critical for us... I think that issue was recently fixed in JDK 15 as https://bugs.openjdk.java.net/browse/JDK-8224109 The primary issue being complained about is reported against JDK 13, so that rules out this fix https://bugs.openjdk.java.net/browse/JDK-8214481 which disables hinting with AA+FM which leaves me puzzled. The other fix that might be implicated : https://bugs.openjdk.java.net/browse/JDK-8217731 since it is fixed in 13, changed the hinting being used to be more like old Oracle JDK 8. But that does not do anything different with Fractional Metrics. In JDK 13 fractional metrics affects the spacing of the glyphs but not the shape of the glyphs. Actually that is the only free type related bug fixed in JDK 13 but I don't see how it would be triggered by fractional metrics Nowhere do I see the platform used by the submitter, or the platform used by the web bugs triager. Nor do I see the font under test (do not tell me "dialog" because that isn't a physical font). You can also try export FREETYPE_PROPERTIES=foo to disable that fix. Make sure to try that on JDK 15 current builds since setting that was not read properly on 13 or 14. So I am bouncing this back to web bugs to report on the platform and the font and confirm it really is new in 13 and triggered by fractional metrics and if possible try the FREETYPE_PROPERTIES seting on 15 (assuming it is still a problem on 15).
25-04-2020

Additional comment from submitter: ============================== although the rendering style as a whole in Java 13 is like one in Java 8, there was no such issue with text weight changing with size in Java 8. Yes it's not specified. Maybe there can be an option to switch it? =============================
05-11-2019

None of this is specified. So there's no regression. It is just different rendering. It is most likely due to a change to render more like JDK 8, in which case it is more like a fix.
05-11-2019

Reported as a regression with OpenJDK 13.0.1, the text weight is reported uneven with the font size in VALUE_FRACTIONALMETRICS_ON mode and any text antialiasing mode on. Note: Regression as it works fine with JDK versions 11.0.5 and 12.0.2. Checked this with provided test case and could confirm that this is a regression in JDK 13. See reference images as variation. To verify, run the linked test case (subsequent comment) with respective JDK versions. Result (Oracle JDK): ===================== 11.0.5: OK 12.0.2: OK 13: Fail 13.0.1: Fail 14 ea b21: Fail Note: Similar results observed in OpenJDK bundles available from jdk.net. Refer to screenshots to identify the change from JDK 12.0.2 to JDK 13.
05-11-2019