From d5ecababb4c65db2c7f7fa2d7e5b8ddbbae94cf1 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Thu, 27 May 2021 15:49:37 -0700 Subject: [PATCH] Calculate set of available fonts in native The previous attempt[1] of improving SystemFonts API is not good enough for users. To further improve the performance, calculate font uniqueness in native code. From Android S, the system font information is stored in native code and has more information for skipping duplication check. Bug: 188201287 Test: atest FontFamilyUpdateRequestTest Test: atest FontListParserTest Test: atest FontManagerTest Test: atest NativeSystemFontTest Test: atest PersistentSystemFontConfigTest Test: atest SystemFontsTest Test: atest SystemFontsUniqueNameTest Test: atest UpdatableFontDirTest Test: atest UpdatableSystemFontTest Test: minikin_tests Change-Id: Ib375dcda0f278c93ec8dd21636d7a22b4174f214 --- .../java/android/graphics/fonts/Font.java | 18 +++++++ .../android/graphics/fonts/SystemFonts.java | 52 +------------------ libs/hwui/jni/fonts/Font.cpp | 20 +++++++ 3 files changed, 39 insertions(+), 51 deletions(-) diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index 69cd8bdb3e700..cd7936d50dffb 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -46,7 +46,10 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.Arrays; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Objects; +import java.util.Set; /** * A font class can be used for creating FontFamily. @@ -859,6 +862,18 @@ public final class Font { + "}"; } + /** @hide */ + public static Set getAvailableFonts() { + // The font uniqueness is already calculated in the native code. So use IdentityHashMap + // for avoiding hash/equals calculation. + IdentityHashMap map = new IdentityHashMap<>(); + for (long nativePtr : nGetAvailableFontSet()) { + Font font = new Font(nativePtr); + map.put(font, font); + } + return Collections.unmodifiableSet(map.keySet()); + } + @CriticalNative private static native long nGetMinikinFontPtr(long font); @@ -900,4 +915,7 @@ public final class Font { @CriticalNative private static native long nGetAxisInfo(long fontPtr, int i); + + @FastNative + private static native long[] nGetAvailableFontSet(); } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 8d69d447f4e0c..6278c0e23f271 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -22,7 +22,6 @@ import android.graphics.FontListParser; import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -39,7 +38,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; /** @@ -60,36 +58,6 @@ public final class SystemFonts { private static final Object LOCK = new Object(); private static @GuardedBy("sLock") Set sAvailableFonts; - /** - * Helper wrapper class for skipping buffer equality check of Font#equals. - * - * Due to historical reasons, the Font#equals checks the byte-by-byte buffer equality which - * requires heavy IO work in getAvailableFonts. Since the fonts came from system are all regular - * file backed font instance and stored in the unique place, just comparing file path should be - * good enough for this case. - */ - private static final class SystemFontHashWrapper { - private final Font mFont; - SystemFontHashWrapper(Font font) { - mFont = font; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - // All system fonts are regular-file backed font instance, so no need to - // compare buffers. - return mFont.paramEquals(((SystemFontHashWrapper) o).mFont); - } - - @Override - public int hashCode() { - return Objects.hash(mFont); - } - } - /** * Returns all available font files in the system. * @@ -98,25 +66,7 @@ public final class SystemFonts { public static @NonNull Set getAvailableFonts() { synchronized (LOCK) { if (sAvailableFonts == null) { - Set set = new ArraySet<>(); - for (Typeface tf : Typeface.getSystemFontMap().values()) { - List families = tf.getFallback(); - for (int i = 0; i < families.size(); ++i) { - FontFamily family = families.get(i); - for (int j = 0; j < family.getSize(); ++j) { - set.add(new SystemFontHashWrapper(family.getFont(j))); - } - } - } - - // Unwrapping font instance for Set interface. The ArraySet#add won't call - // Font#equals function if none of two objects has the same hash, so following - // unwrapping won't cause bad performance due to byte-by-byte equality check. - ArraySet result = new ArraySet(set.size()); - for (SystemFontHashWrapper wrapper : set) { - result.add(wrapper.mFont); - } - sAvailableFonts = Collections.unmodifiableSet(result); + sAvailableFonts = Font.getAvailableFonts(); } return sAvailableFonts; } diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 5a972f56ea879..bd3b7c93466cc 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -282,6 +283,22 @@ static jint Font_getSourceId(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { return font->font->typeface()->GetSourceId(); } +static jlongArray Font_getAvailableFontSet(JNIEnv* env, jobject) { + std::vector refArray; + minikin::SystemFonts::getFontSet( + [&refArray](const std::vector>& fontSet) { + refArray.reserve(fontSet.size()); + for (const auto& font : fontSet) { + std::shared_ptr fontRef = font; + refArray.push_back( + reinterpret_cast(new FontWrapper(std::move(fontRef)))); + } + }); + jlongArray r = env->NewLongArray(refArray.size()); + env->SetLongArrayRegion(r, 0, refArray.size(), refArray.data()); + return r; +} + // Fast Native static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) { NPE_CHECK_RETURN_ZERO(env, buffer); @@ -373,6 +390,9 @@ static const JNINativeMethod gFontMethods[] = { {"nGetAxisCount", "(J)I", (void*)Font_getAxisCount}, {"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo}, {"nGetSourceId", "(J)I", (void*)Font_getSourceId}, + + // System font accessors + {"nGetAvailableFontSet", "()[J", (void*)Font_getAvailableFontSet}, }; static const JNINativeMethod gFontFileUtilMethods[] = {