From fb95699364e555148b437cfa1e5c69384f843845 Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Thu, 28 Jan 2016 16:05:33 -0500 Subject: [PATCH] Deduplicate font file mappings. With ttc and gx variation fonts, it is now possible and common that a number of fonts will use the same font file for data but with different parameters. In the current code each font will map the font file data, taking up an unecessary amount of virtual address space and is inefficient with respect to memory management (like the tlb). This CL deduplicates these file mappings so that a given font file will only be mapped into memory once. DO NOT MERGE Change-Id: I5ca69f963a434c72ec4028402ecbf9e0f0ee7148 (cherry picked from commit fffcf0a31fd4c9a4ec8aa7de70b1eda0d48fb337) --- core/jni/android/graphics/FontFamily.cpp | 52 +++++++++++++++---- .../java/android/graphics/FontFamily.java | 7 +-- graphics/java/android/graphics/Typeface.java | 29 +++++++++-- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 149ff557645da..2e974a3ecaa38 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -26,6 +26,7 @@ #include "GraphicsJNI.h" #include #include +#include #include #include #include "Utils.h" @@ -82,9 +83,32 @@ static struct { jfieldID mStyleValue; } gAxisClassInfo; +static void release_global_ref(const void* /*data*/, void* context) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + bool needToAttach = (env == NULL); + if (needToAttach) { + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_4; + args.name = "release_font_data"; + args.group = NULL; + jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args); + if (result != JNI_OK) { + ALOGE("failed to attach to thread to release global ref."); + return; + } + } + + jobject obj = reinterpret_cast(context); + env->DeleteGlobalRef(obj); + + if (needToAttach) { + AndroidRuntime::getJavaVM()->DetachCurrentThread(); + } +} + static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr, - jstring path, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) { - NPE_CHECK_RETURN_ZERO(env, path); + jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) { + NPE_CHECK_RETURN_ZERO(env, font); // Declare axis native type. std::unique_ptr skiaAxes; @@ -109,19 +133,29 @@ static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong } } - ScopedUtfChars str(env, path); - SkAutoTUnref fm(SkFontMgr::RefDefault()); - std::unique_ptr fontData(SkStream::NewFromFile(str.c_str())); - if (!fontData) { - ALOGE("addFont failed to open %s", str.c_str()); + void* fontPtr = env->GetDirectBufferAddress(font); + if (fontPtr == NULL) { + ALOGE("addFont failed to create font, buffer invalid"); return false; } + jlong fontSize = env->GetDirectBufferCapacity(font); + if (fontSize < 0) { + ALOGE("addFont failed to create font, buffer size invalid"); + return false; + } + jobject fontRef = MakeGlobalRefOrDie(env, font); + SkAutoTUnref data(SkData::NewWithProc(fontPtr, fontSize, + release_global_ref, reinterpret_cast(fontRef))); + std::unique_ptr fontData(new SkMemoryStream(data)); + SkFontMgr::FontParameters params; params.setCollectionIndex(ttcIndex); params.setAxes(skiaAxes.get(), skiaAxesLength); + + SkAutoTUnref fm(SkFontMgr::RefDefault()); SkTypeface* face = fm->createFromStream(fontData.release(), params); if (face == NULL) { - ALOGE("addFont failed to create font %s#%d", str.c_str(), ttcIndex); + ALOGE("addFont failed to create font, invalid request"); return false; } FontFamily* fontFamily = reinterpret_cast(familyPtr); @@ -175,7 +209,7 @@ static const JNINativeMethod gFontFamilyMethods[] = { { "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create }, { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, { "nAddFont", "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont }, - { "nAddFontWeightStyle", "(JLjava/lang/String;ILjava/util/List;IZ)Z", + { "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z", (void*)FontFamily_addFontWeightStyle }, { "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z", (void*)FontFamily_addFontFromAsset }, diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index 3897c3bf45d8b..f741e3cb5f8de 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -18,6 +18,7 @@ package android.graphics; import android.content.res.AssetManager; +import java.nio.ByteBuffer; import java.util.List; /** @@ -64,9 +65,9 @@ public class FontFamily { return nAddFont(mNativePtr, path, ttcIndex); } - public boolean addFontWeightStyle(String path, int ttcIndex, List axes, + public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List axes, int weight, boolean style) { - return nAddFontWeightStyle(mNativePtr, path, ttcIndex, axes, weight, style); + return nAddFontWeightStyle(mNativePtr, font, ttcIndex, axes, weight, style); } public boolean addFontFromAsset(AssetManager mgr, String path) { @@ -76,7 +77,7 @@ public class FontFamily { private static native long nCreateFamily(String lang, int variant); private static native void nUnrefFamily(long nativePtr); private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex); - private static native boolean nAddFontWeightStyle(long nativeFamily, String path, + private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font, int ttcIndex, List listOfAxis, int weight, boolean isItalic); private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 345f257c9371f..f15aff7de39c2 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -27,6 +27,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -258,11 +260,26 @@ public class Typeface { mStyle = nativeGetStyle(ni); } - private static FontFamily makeFamilyFromParsed(FontListParser.Family family) { + private static FontFamily makeFamilyFromParsed(FontListParser.Family family, + Map bufferForPath) { FontFamily fontFamily = new FontFamily(family.lang, family.variant); for (FontListParser.Font font : family.fonts) { - fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.axes, - font.weight, font.isItalic); + ByteBuffer fontBuffer = bufferForPath.get(font.fontName); + if (fontBuffer == null) { + try (FileInputStream file = new FileInputStream(font.fontName)) { + FileChannel fileChannel = file.getChannel(); + long fontSize = fileChannel.size(); + fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); + bufferForPath.put(font.fontName, fontBuffer); + } catch (IOException e) { + Log.e(TAG, "Error mapping font file " + font.fontName); + continue; + } + } + if (!fontFamily.addFontWeightStyle(fontBuffer, font.ttcIndex, font.axes, + font.weight, font.isItalic)) { + Log.e(TAG, "Error creating font " + font.fontName + "#" + font.ttcIndex); + } } return fontFamily; } @@ -280,13 +297,15 @@ public class Typeface { FileInputStream fontsIn = new FileInputStream(configFilename); FontListParser.Config fontConfig = FontListParser.parse(fontsIn); + Map bufferForPath = new HashMap(); + List familyList = new ArrayList(); // Note that the default typeface is always present in the fallback list; // this is an enhancement from pre-Minikin behavior. for (int i = 0; i < fontConfig.families.size(); i++) { FontListParser.Family f = fontConfig.families.get(i); if (i == 0 || f.name == null) { - familyList.add(makeFamilyFromParsed(f)); + familyList.add(makeFamilyFromParsed(f, bufferForPath)); } } sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]); @@ -302,7 +321,7 @@ public class Typeface { // duplicating the corresponding FontFamily. typeface = sDefaultTypeface; } else { - FontFamily fontFamily = makeFamilyFromParsed(f); + FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath); FontFamily[] families = { fontFamily }; typeface = Typeface.createFromFamiliesWithDefault(families); }