Add necessary APIs for supporting glyph level drawing

This CL adds following APIs

Font#cloneWithSettings
  This API provides a faster Font instance creation for different
  variation settings.

Font#getBounds
  This API provides glyph advance and bounding box information.

Font#getMetrics
  This API provides font metrics that will be used for deciding line
  height and/or baseline.

Bug: 168136332
Test: atest FontTest
Test: TreeHugger

Change-Id: I335557ce47ea0ca8e012c2a48b804c00bb348392
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index ecbb55ec..77f46be 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -9,6 +9,7 @@
 #include "GraphicsJNI.h"
 
 #include "SkCanvas.h"
+#include "SkFontMetrics.h"
 #include "SkMath.h"
 #include "SkRegion.h"
 #include <cutils/ashmem.h>
@@ -228,6 +229,20 @@
 static jclass gTransferParameters_class;
 static jmethodID gTransferParameters_constructorMethodID;
 
+static jclass   gFontMetrics_class;
+static jfieldID gFontMetrics_top;
+static jfieldID gFontMetrics_ascent;
+static jfieldID gFontMetrics_descent;
+static jfieldID gFontMetrics_bottom;
+static jfieldID gFontMetrics_leading;
+
+static jclass   gFontMetricsInt_class;
+static jfieldID gFontMetricsInt_top;
+static jfieldID gFontMetricsInt_ascent;
+static jfieldID gFontMetricsInt_descent;
+static jfieldID gFontMetricsInt_bottom;
+static jfieldID gFontMetricsInt_leading;
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
@@ -468,6 +483,32 @@
     return r;
 }
 
+void GraphicsJNI::set_metrics(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
+    if (metrics == nullptr) return;
+    SkASSERT(env->IsInstanceOf(metrics, gFontMetrics_class));
+    env->SetFloatField(metrics, gFontMetrics_top, SkScalarToFloat(skmetrics.fTop));
+    env->SetFloatField(metrics, gFontMetrics_ascent, SkScalarToFloat(skmetrics.fAscent));
+    env->SetFloatField(metrics, gFontMetrics_descent, SkScalarToFloat(skmetrics.fDescent));
+    env->SetFloatField(metrics, gFontMetrics_bottom, SkScalarToFloat(skmetrics.fBottom));
+    env->SetFloatField(metrics, gFontMetrics_leading, SkScalarToFloat(skmetrics.fLeading));
+}
+
+int GraphicsJNI::set_metrics_int(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
+    int ascent = SkScalarRoundToInt(skmetrics.fAscent);
+    int descent = SkScalarRoundToInt(skmetrics.fDescent);
+    int leading = SkScalarRoundToInt(skmetrics.fLeading);
+
+    if (metrics) {
+        SkASSERT(env->IsInstanceOf(metrics, gFontMetricsInt_class));
+        env->SetIntField(metrics, gFontMetricsInt_top, SkScalarFloorToInt(skmetrics.fTop));
+        env->SetIntField(metrics, gFontMetricsInt_ascent, ascent);
+        env->SetIntField(metrics, gFontMetricsInt_descent, descent);
+        env->SetIntField(metrics, gFontMetricsInt_bottom, SkScalarCeilToInt(skmetrics.fBottom));
+        env->SetIntField(metrics, gFontMetricsInt_leading, leading);
+    }
+    return descent - ascent + leading;
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////
 
 jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, skia::BitmapRegionDecoder* bitmap)
@@ -764,5 +805,23 @@
     gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class,
             "<init>", "(DDDDDDD)V");
 
+    gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
+    gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
+
+    gFontMetrics_top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
+    gFontMetrics_ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
+    gFontMetrics_descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
+    gFontMetrics_bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
+    gFontMetrics_leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
+
+    gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
+    gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
+
+    gFontMetricsInt_top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
+    gFontMetricsInt_ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
+    gFontMetricsInt_descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
+    gFontMetricsInt_bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
+    gFontMetricsInt_leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
+
     return 0;
 }
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index 79ab617..541d5a5 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -18,6 +18,7 @@
 #include "graphics_jni_helpers.h"
 
 class SkCanvas;
+struct SkFontMetrics;
 
 namespace android {
 namespace skia {
@@ -85,6 +86,17 @@
                                      bool* isHardware);
     static SkRegion* getNativeRegion(JNIEnv*, jobject region);
 
+    /**
+     * Set SkFontMetrics to Java Paint.FontMetrics.
+     * Do nothing if metrics is nullptr.
+     */
+    static void set_metrics(JNIEnv*, jobject metrics, const SkFontMetrics& skmetrics);
+    /**
+     * Set SkFontMetrics to Java Paint.FontMetricsInt and return recommended interline space.
+     * Do nothing if metrics is nullptr.
+     */
+    static int set_metrics_int(JNIEnv*, jobject metrics, const SkFontMetrics& skmetrics);
+
     /*
      *  LegacyBitmapConfig is the old enum in Skia that matched the enum int values
      *  in Bitmap.Config. Skia no longer supports this config, but has replaced it
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 554674a..d275659 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -59,20 +59,6 @@
 
 namespace android {
 
-struct JMetricsID {
-    jfieldID    top;
-    jfieldID    ascent;
-    jfieldID    descent;
-    jfieldID    bottom;
-    jfieldID    leading;
-};
-
-static jclass   gFontMetrics_class;
-static JMetricsID gFontMetrics_fieldID;
-
-static jclass   gFontMetricsInt_class;
-static JMetricsID gFontMetricsInt_fieldID;
-
 static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count,
                            const SkPoint pos[], SkPath* dst) {
     dst->reset();
@@ -618,35 +604,14 @@
     static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
         SkFontMetrics metrics;
         SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
-
-        if (metricsObj) {
-            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
-        }
+        GraphicsJNI::set_metrics(env, metricsObj, metrics);
         return SkScalarToFloat(spacing);
     }
 
     static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
         SkFontMetrics metrics;
-
         getMetricsInternal(paintHandle, &metrics);
-        int ascent = SkScalarRoundToInt(metrics.fAscent);
-        int descent = SkScalarRoundToInt(metrics.fDescent);
-        int leading = SkScalarRoundToInt(metrics.fLeading);
-
-        if (metricsObj) {
-            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
-        }
-        return descent - ascent + leading;
+        return GraphicsJNI::set_metrics_int(env, metricsObj, metrics);
     }
 
 
@@ -1137,24 +1102,6 @@
 };
 
 int register_android_graphics_Paint(JNIEnv* env) {
-    gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
-    gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
-
-    gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
-    gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
-    gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
-    gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
-    gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
-
-    gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
-    gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
-
-    gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
-    gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
-    gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
-    gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
-    gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
-
     return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
 }
 
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 996cdce..bcdb4f5 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "Minikin"
 
 #include "SkData.h"
+#include "SkFont.h"
+#include "SkFontMetrics.h"
 #include "SkFontMgr.h"
 #include "SkRefCnt.h"
 #include "SkTypeface.h"
@@ -27,6 +29,7 @@
 #include "FontUtils.h"
 
 #include <hwui/MinikinSkia.h>
+#include <hwui/Paint.h>
 #include <hwui/Typeface.h>
 #include <minikin/FontFamily.h>
 #include <ui/FatVector.h>
@@ -120,6 +123,36 @@
     return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
 }
 
+// Fast Native
+static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong builderPtr,
+                                jint weight, jboolean italic, jint ttcIndex) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+    std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
+
+    // Reconstruct SkTypeface with different arguments from existing SkTypeface.
+    FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
+    for (const auto& axis : builder->axes) {
+        skVariation.push_back({axis.axisTag, axis.value});
+    }
+    SkFontArguments args;
+    args.setCollectionIndex(ttcIndex);
+    args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
+
+    sk_sp<SkTypeface> newTypeface = minikinSkia->GetSkTypeface()->makeClone(args);
+
+    std::shared_ptr<minikin::MinikinFont> newMinikinFont = std::make_shared<MinikinFontSkia>(
+        std::move(newTypeface),
+        minikinSkia->GetFontData(),
+        minikinSkia->GetFontSize(),
+        minikinSkia->getFilePath(),
+        minikinSkia->GetFontIndex(),
+        builder->axes);
+    minikin::Font newFont = minikin::Font::Builder(newMinikinFont).setWeight(weight)
+              .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+    return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont)));
+}
+
 // Critical Native
 static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
     return reinterpret_cast<jlong>(releaseFont);
@@ -127,16 +160,64 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// Fast Native
+static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
+                                  jlong paintHandle, jobject rect) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+    SkFont* skFont = &paint->getSkFont();
+    // We don't use populateSkFont since it is designed to be used for layout result with addressing
+    // auto fake-bolding.
+    skFont->setTypeface(minikinSkia->RefSkTypeface());
+
+    uint16_t glyph16 = glyphId;
+    SkRect skBounds;
+    SkScalar skWidth;
+    skFont->getWidthsBounds(&glyph16, 1, &skWidth, &skBounds, nullptr);
+    GraphicsJNI::rect_to_jrectf(skBounds, env, rect);
+    return SkScalarToFloat(skWidth);
+}
+
+// Fast Native
+static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong paintHandle,
+                                  jobject metricsObj) {
+    FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+    MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+    SkFont* skFont = &paint->getSkFont();
+    // We don't use populateSkFont since it is designed to be used for layout result with addressing
+    // auto fake-bolding.
+    skFont->setTypeface(minikinSkia->RefSkTypeface());
+
+    SkFontMetrics metrics;
+    SkScalar spacing = skFont->getMetrics(&metrics);
+    GraphicsJNI::set_metrics(env, metricsObj, metrics);
+    return spacing;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 static const JNINativeMethod gFontBuilderMethods[] = {
     { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
     { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
     { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
+    { "nClone", "(JJIZI)J", (void*) Font_Builder_clone },
     { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
 };
 
+static const JNINativeMethod gFontMethods[] = {
+    { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
+    { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
+};
+
 int register_android_graphics_fonts_Font(JNIEnv* env) {
     return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
-            NELEM(gFontBuilderMethods));
+            NELEM(gFontBuilderMethods)) +
+            RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
+            NELEM(gFontMethods));
 }
 
 }