changeset 9903:8eeff57878e0

8218854: FontMetrics.getMaxAdvance may be less than the maximum FontMetrics.charWidth Summary: Consider algorithmic bold in FontMetrics.getMaxAdvance value and update obliqueness. Reviewed-by: prr, serb, andrew
author mbalao
date Fri, 18 Oct 2019 16:56:30 -0300
parents e4e8739563d1
children 3fb2af3bcafc
files src/share/native/sun/font/freetypeScaler.c test/java/awt/FontMetrics/MaxAdvanceIsMax.java
diffstat 2 files changed, 158 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/native/sun/font/freetypeScaler.c	Tue Feb 04 03:47:35 2020 +0000
+++ b/src/share/native/sun/font/freetypeScaler.c	Fri Oct 18 16:56:30 2019 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, 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
@@ -448,10 +448,18 @@
     return errCode;
 }
 
-/* ftsynth.c uses (0x10000, 0x06000, 0x0, 0x10000) matrix to get oblique
-   outline.  Therefore x coordinate will change by 0x06000*y.
-   Note that y coordinate does not change. */
-#define OBLIQUE_MODIFIER(y)  (context->doItalize ? ((y)*6/16) : 0)
+/* ftsynth.c uses (0x10000, 0x0366A, 0x0, 0x10000) matrix to get oblique
+   outline.  Therefore x coordinate will change by 0x0366A*y.
+   Note that y coordinate does not change. These values are based on
+   libfreetype version 2.9.1. */
+#define OBLIQUE_MODIFIER(y)  (context->doItalize ? ((y)*0x366A/0x10000) : 0)
+
+/* FT_GlyphSlot_Embolden (ftsynth.c) uses FT_MulFix(units_per_EM, y_scale) / 24
+ * strength value when glyph format is FT_GLYPH_FORMAT_OUTLINE. This value has
+ * been taken from libfreetype version 2.6 and remain valid at least up to
+ * 2.9.1. */
+#define BOLD_MODIFIER(units_per_EM, y_scale) \
+    (context->doBold ? FT_MulFix(units_per_EM, y_scale) / 24 : 0)
 
 /*
  * Class:     sun_font_FreetypeFontScaler
@@ -530,7 +538,9 @@
     /* max advance */
     mx = (jfloat) FT26Dot6ToFloat(
                      scalerInfo->face->size->metrics.max_advance +
-                     OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height));
+                     OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height) +
+                     BOLD_MODIFIER(scalerInfo->face->units_per_EM,
+                             scalerInfo->face->size->metrics.y_scale));
     my = 0;
 
     metrics = (*env)->NewObject(env,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/awt/FontMetrics/MaxAdvanceIsMax.java	Fri Oct 18 16:56:30 2019 -0300
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2019, Red Hat, Inc.
+ * 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 8218854
+ * @run main/othervm MaxAdvanceIsMax
+ */
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+
+public class MaxAdvanceIsMax {
+
+    private static boolean debug = true;
+
+    private static final class AntialiasHint {
+        private Object aaHint;
+        private String asString = "";
+
+        AntialiasHint(Object aaHint) {
+            if (aaHint.equals(
+                    RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)) {
+                asString += "FT_LOAD_TARGET_MONO";
+            } else if (aaHint.equals(
+                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON)) {
+                asString += "FT_LOAD_TARGET_NORMAL";
+            } else if (aaHint.equals(
+                    RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB)) {
+                asString += "FT_LOAD_TARGET_LCD";
+            } else if (aaHint.equals(
+                    RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB)) {
+                asString += "FT_LOAD_TARGET_LCD_V";
+            }
+            this.aaHint = aaHint;
+        }
+
+        public Object getHint() {
+            return aaHint;
+        }
+
+        public String toString() {
+            return asString;
+        }
+    }
+
+    private static final AntialiasHint[] antialiasHints = {
+            new AntialiasHint(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF),
+            new AntialiasHint(RenderingHints.VALUE_TEXT_ANTIALIAS_ON),
+            new AntialiasHint(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB),
+            new AntialiasHint(RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB)
+    };
+
+    private static final class StyleAndSize {
+        int style;
+        float size;
+        public StyleAndSize(int style, float size) {
+            this.style = style;
+            this.size = size;
+        }
+    };
+
+    private static final StyleAndSize[] stylesAndSizes = new StyleAndSize[] {
+        new StyleAndSize(Font.BOLD | Font.ITALIC, 10)
+    };
+
+    public static void main(String[] args) throws Exception {
+        GraphicsEnvironment e =
+                GraphicsEnvironment.getLocalGraphicsEnvironment();
+        Font[] fonts = e.getAllFonts();
+        BufferedImage bi = new BufferedImage(500, 500,
+                BufferedImage.TYPE_INT_RGB);
+        for (AntialiasHint antialiasHint : antialiasHints) {
+            for (Font f : fonts) {
+                for (StyleAndSize styleAndSize : stylesAndSizes) {
+                    f = f.deriveFont(styleAndSize.style, styleAndSize.size);
+                    Graphics2D g2d = bi.createGraphics();
+                    g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+                            antialiasHint.getHint());
+                    FontMetrics fm = g2d.getFontMetrics(f);
+                    int[] width;
+                    int maxWidth = -1;
+                    int maxAdvance = fm.getMaxAdvance();
+                    if (debug) {
+                        System.out.println("Testing " + f + " in " +
+                                antialiasHint);
+                        System.out.println("getMaxAdvance: " + maxAdvance);
+                    }
+                    if (maxAdvance != -1) {
+                        String failureMessage = null;
+                        width = fm.getWidths();
+                        for (int j = 0; j < width.length; j++) {
+                            if (width[j] > maxWidth) {
+                                maxWidth = width[j];
+                            }
+                            if (width[j] > maxAdvance) {
+                                failureMessage = "FAILED: getMaxAdvance is " +
+                                                 "not max for font: " +
+                                                 f.toString() +
+                                                 " getMaxAdvance(): " +
+                                                 maxAdvance +
+                                                 " getWidths()[" + j + "]: " +
+                                                 width[j];
+                                throw new Exception(failureMessage);
+                            }
+                        }
+                    }
+                    if (debug) {
+                        System.out.println("Max char width: " + maxWidth);
+                        System.out.println("PASSED");
+                        System.out.println(".........................");
+                    }
+                }
+            }
+        }
+        System.out.println("TEST PASS - OK");
+    }
+}