8233097: Fontmetrics for large Fonts has zero width
authorprr
Mon, 04 Nov 2019 10:01:55 -0800
changeset 59176 f5adbf111424
parent 59175 d2123a27cfe7
child 59177 afda8f7d266c
8233097: Fontmetrics for large Fonts has zero width Reviewed-by: jdv, serb
src/java.desktop/share/native/libfontmanager/freetypeScaler.c
test/jdk/java/awt/FontClass/MassiveMetricsTest.java
--- a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c	Thu Oct 24 14:54:31 2019 -0700
+++ b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c	Mon Nov 04 10:01:55 2019 -0800
@@ -611,6 +611,12 @@
     return metrics;
 }
 
+static jlong
+    getGlyphImageNativeInternal(
+        JNIEnv *env, jobject scaler, jobject font2D,
+        jlong pScalerContext, jlong pScaler, jint glyphCode,
+        jboolean renderImage);
+
 /*
  * Class:     sun_font_FreetypeFontScaler
  * Method:    getGlyphAdvanceNative
@@ -622,24 +628,23 @@
         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 
    /* This method is rarely used because requests for metrics are usually
-      coupled with request for bitmap and to large extend work can be reused
-      (to find out metrics we need to hint glyph).
-      So, we typically go through getGlyphImage code path.
-
-      For initial freetype implementation we delegate
-      all work to getGlyphImage but drop result image.
-      This is waste of work related to scan conversion and conversion from
-      freetype format to our format but for now this seems to be ok.
-
-      NB: investigate performance benefits of refactoring code
-      to avoid unnecesary work with bitmaps. */
+    * coupled with a request for the bitmap and to a large extent the
+    * work can be reused (to find out metrics we may need to hint the glyph).
+    * So, we typically go through the getGlyphImage code path.
+    * When we do get here, we need to pass a parameter which indicates
+    * that we don't need freetype to render the bitmap, and consequently
+    * don't need to allocate our own storage either.
+    * This is also important when enter here requesting metrics for sizes
+    * of text which a large size would be rejected for a bitmap but we
+    * still need the metrics.
+    */
 
     GlyphInfo *info;
     jfloat advance = 0.0f;
     jlong image;
 
-    image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
-                 env, scaler, font2D, pScalerContext, pScaler, glyphCode);
+    image = getGlyphImageNativeInternal(
+          env, scaler, font2D, pScalerContext, pScaler, glyphCode, JNI_FALSE);
     info = (GlyphInfo*) jlong_to_ptr(image);
 
     if (info != NULL) {
@@ -660,17 +665,12 @@
         JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
         jlong pScaler, jint glyphCode, jobject metrics) {
 
-     /* As initial implementation we delegate all work to getGlyphImage
-        but drop result image. This is clearly waste of resorces.
-
-        TODO: investigate performance benefits of refactoring code
-              by avoiding bitmap generation and conversion from FT
-              bitmap format. */
+     /* See the comments in getGlyphMetricsNative. They apply here too. */
      GlyphInfo *info;
 
-     jlong image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
+     jlong image = getGlyphImageNativeInternal(
                                  env, scaler, font2D,
-                                 pScalerContext, pScaler, glyphCode);
+                                 pScalerContext, pScaler, glyphCode, JNI_FALSE);
      info = (GlyphInfo*) jlong_to_ptr(image);
 
      if (info != NULL) {
@@ -804,6 +804,17 @@
         JNIEnv *env, jobject scaler, jobject font2D,
         jlong pScalerContext, jlong pScaler, jint glyphCode) {
 
+    return getGlyphImageNativeInternal(
+        env, scaler, font2D,
+        pScalerContext, pScaler, glyphCode, JNI_TRUE);
+}
+
+static jlong
+     getGlyphImageNativeInternal(
+        JNIEnv *env, jobject scaler, jobject font2D,
+        jlong pScalerContext, jlong pScaler, jint glyphCode,
+        jboolean renderImage) {
+
     int error, imageSize;
     UInt16 width, height;
     GlyphInfo *glyphInfo;
@@ -866,7 +877,7 @@
 
     /* generate bitmap if it is not done yet
      e.g. if algorithmic styling is performed and style was added to outline */
-    if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+    if (renderImage && (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE)) {
         FT_BBox bbox;
         FT_Outline_Get_CBox(&(ftglyph->outline), &bbox);
         int w = (int)((bbox.xMax>>6)-(bbox.xMin>>6));
@@ -881,12 +892,17 @@
         }
     }
 
-    width  = (UInt16) ftglyph->bitmap.width;
-    height = (UInt16) ftglyph->bitmap.rows;
-    if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) {
-        glyphInfo = getNullGlyphImage();
-        return ptr_to_jlong(glyphInfo);
-    }
+    if (renderImage) {
+        width  = (UInt16) ftglyph->bitmap.width;
+        height = (UInt16) ftglyph->bitmap.rows;
+            if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) {
+              glyphInfo = getNullGlyphImage();
+              return ptr_to_jlong(glyphInfo);
+            }
+     } else {
+        width = 0;
+        height = 0;
+     }
 
 
     imageSize = width*height;
@@ -900,13 +916,16 @@
     glyphInfo->rowBytes  = width;
     glyphInfo->width     = width;
     glyphInfo->height    = height;
-    glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
-    glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
+
+    if (renderImage) {
+        glyphInfo->topLeftX  = (float)  ftglyph->bitmap_left;
+        glyphInfo->topLeftY  = (float) -ftglyph->bitmap_top;
 
-    if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
-        glyphInfo->width = width/3;
-    } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
-        glyphInfo->height = glyphInfo->height/3;
+        if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD) {
+            glyphInfo->width = width/3;
+        } else if (ftglyph->bitmap.pixel_mode ==  FT_PIXEL_MODE_LCD_V) {
+            glyphInfo->height = glyphInfo->height/3;
+        }
     }
 
     if (context->fmType == TEXT_FM_ON) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/awt/FontClass/MassiveMetricsTest.java	Mon Nov 04 10:01:55 2019 -0800
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 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
+ * 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 8233097
+ * @summary Test we get non-zero metrics with large sizes.
+ * @run main MassiveMetricsTest
+ */
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+
+public class MassiveMetricsTest {
+
+    public static void main(String [] args) {
+
+        GraphicsEnvironment ge =
+            GraphicsEnvironment.getLocalGraphicsEnvironment();
+        Font[] fonts = ge.getAllFonts();
+        BufferedImage bi = new BufferedImage(1,1,1);
+        Graphics2D g2d = bi.createGraphics();
+        int[] sizes = { 80, 100, 120, 600, 1600, 2400, 3600, 7200, 12000 };
+        String s = "m";
+
+        for (Font f : fonts) {
+            Font sz12Font = f.deriveFont(Font.PLAIN, 12);
+            FontMetrics sz12 = g2d.getFontMetrics(sz12Font);
+            if (sz12.stringWidth(s) == 0) {
+                continue; // code point not supported or similar.
+            }
+            boolean fail = false;
+            for (int sz : sizes) {
+                Font font = f.deriveFont(Font.PLAIN, sz);
+                FontMetrics fm = g2d.getFontMetrics(font);
+                if (fm.stringWidth(s) == 0) {
+                  fail = true;
+                  System.err.println("zero for " + font);
+                }
+            }
+            if (fail) {
+                throw new RuntimeException("Zero stringwidth");
+            }
+        }
+    }
+}