diff --git a/api/current.txt b/api/current.txt index 93eb32caf6976..fafd3708d66cb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16424,9 +16424,9 @@ package android.graphics.fonts { method public float getGlyphBounds(@IntRange(from=0) int, @NonNull android.graphics.Paint, @Nullable android.graphics.RectF); method @NonNull public android.os.LocaleList getLocaleList(); method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics); + method public int getSourceIdentifier(); method @NonNull public android.graphics.fonts.FontStyle getStyle(); method @IntRange(from=0) public int getTtcIndex(); - method public boolean isSameSource(@NonNull android.graphics.fonts.Font); } public static final class Font.Builder { diff --git a/core/api/current.txt b/core/api/current.txt index 9329e2ae47fc4..86a0f3b1ccf5e 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -16406,9 +16406,9 @@ package android.graphics.fonts { method public float getGlyphBounds(@IntRange(from=0) int, @NonNull android.graphics.Paint, @Nullable android.graphics.RectF); method @NonNull public android.os.LocaleList getLocaleList(); method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics); + method public int getSourceIdentifier(); method @NonNull public android.graphics.fonts.FontStyle getStyle(); method @IntRange(from=0) public int getTtcIndex(); - method public boolean isSameSource(@NonNull android.graphics.fonts.Font); } public static final class Font.Builder { diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index c07b4bcf0e729..e7c15821e5b33 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -28,6 +28,7 @@ import android.os.LocaleList; import android.os.ParcelFileDescriptor; import android.util.Log; import android.util.LongSparseArray; +import android.util.LongSparseLongArray; import android.util.TypedValue; import com.android.internal.annotations.GuardedBy; @@ -69,6 +70,11 @@ public final class Font { private static final LongSparseArray> FONT_PTR_MAP = new LongSparseArray<>(); + private static final Object SOURCE_ID_LOCK = new Object(); + @GuardedBy("SOURCE_ID_LOCK") + private static final LongSparseLongArray FONT_SOURCE_ID_MAP = + new LongSparseLongArray(300); // System font has 200 fonts, so 300 should be enough. + /** * A builder class for creating new Font. */ @@ -465,13 +471,24 @@ public final class Font { final String filePath = mFile == null ? "" : mFile.getAbsolutePath(); long ptr; + int fontIdentifier; if (mFont == null) { ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic, mTtcIndex); + long fontBufferPtr = nGetFontBufferAddress(ptr); + synchronized (SOURCE_ID_LOCK) { + long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1); + if (id == -1) { + id = FONT_SOURCE_ID_MAP.size(); + FONT_SOURCE_ID_MAP.put(fontBufferPtr, id); + } + fontIdentifier = (int) id; + } } else { ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex); + fontIdentifier = mFont.mSourceIdentifier; } final Font font = new Font(ptr, readonlyBuffer, mFile, - new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList); + new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList, fontIdentifier); sFontRegistry.registerNativeAllocation(font, ptr); return font; } @@ -500,13 +517,15 @@ public final class Font { private final @IntRange(from = 0) int mTtcIndex; private final @Nullable FontVariationAxis[] mAxes; private final @NonNull String mLocaleList; + private final int mSourceIdentifier; // An identifier of font source data. /** * Use Builder instead */ private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file, @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex, - @Nullable FontVariationAxis[] axes, @NonNull String localeList) { + @Nullable FontVariationAxis[] axes, @NonNull String localeList, + int sourceIdentifier) { mBuffer = buffer; mFile = file; mFontStyle = fontStyle; @@ -514,6 +533,7 @@ public final class Font { mTtcIndex = ttcIndex; mAxes = axes; mLocaleList = localeList; + mSourceIdentifier = sourceIdentifier; synchronized (MAP_LOCK) { FONT_PTR_MAP.append(nGetNativeFontPtr(mNativePtr), new WeakReference<>(this)); @@ -626,6 +646,62 @@ public final class Font { return mNativePtr; } + /** + * Returns the unique ID of the source font data. + * + * You can use this identifier as a key of the cache or checking if two fonts can be + * interpolated with font variation settings. + *
+     * 
+     *   // Following three Fonts, fontA, fontB, fontC have the same identifier.
+     *   Font fontA = new Font.Builder("/path/to/font").build();
+     *   Font fontB = new Font.Builder(fontA).setTtcIndex(1).build();
+     *   Font fontC = new Font.Builder(fontB).setFontVariationSettings("'wght' 700).build();
+     *
+     *   // Following fontD has the different identifier from above three.
+     *   Font fontD = new Font.Builder("/path/to/another/font").build();
+     *
+     *   // Following fontE has different identifier from above four even the font path is the same.
+     *   // To get the same identifier, please create new Font instance from existing fonts.
+     *   Font fontE = new Font.Builder("/path/to/font").build();
+     * 
+     * 
+ * + * Here is an example of caching font object that has + *
+     * 
+     *   private LongSparseArray> mCache = new LongSparseArray<>();
+     *
+     *   private Font getFontWeightVariation(Font font, int weight) {
+     *       // Different collection index is treated as different font.
+     *       long key = ((long) font.getSourceIdentifier()) << 32 | (long) font.getTtcIndex();
+     *
+     *       SparseArray weightCache = mCache.get(key);
+     *       if (weightCache == null) {
+     *          weightCache = new SparseArray<>();
+     *          mCache.put(key, weightCache);
+     *       }
+     *
+     *       Font cachedFont = weightCache.get(weight);
+     *       if (cachedFont != null) {
+     *         return cachedFont;
+     *       }
+     *
+     *       Font newFont = new Font.Builder(cachedFont)
+     *           .setFontVariationSettings("'wght' " + weight);
+     *           .build();
+     *
+     *       weightCache.put(weight, newFont);
+     *       return newFont;
+     *   }
+     * 
+     * 
+ * @return an unique identifier for the font source data. + */ + public int getSourceIdentifier() { + return mSourceIdentifier; + } + /** * Returns true if the given font is created from the same source data from this font. * @@ -643,7 +719,7 @@ public final class Font { * @param other a font object to be compared. * @return true if given font is created from the same source from this font. Otherwise false. */ - public boolean isSameSource(@NonNull Font other) { + private boolean isSameSource(@NonNull Font other) { Objects.requireNonNull(other); // Shortcut for the same instance. @@ -660,7 +736,7 @@ public final class Font { // underlying native font object holds buffer address, check if this buffer points exactly // the same address as a shortcut of equality. For being compatible with of API30 or before, // check buffer position even if the buffer points the same address. - if (nIsSameBufferAddress(mNativePtr, other.mNativePtr) + if (mSourceIdentifier == other.mSourceIdentifier && mBuffer.position() == other.mBuffer.position()) { return true; } @@ -788,5 +864,5 @@ public final class Font { private static native long nGetNativeFontPtr(long ptr); @CriticalNative - private static native boolean nIsSameBufferAddress(long lFontPtr, long rFontPtr); + private static native long nGetFontBufferAddress(long font); } diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 4aee6b94a2beb..21fcd2ff375b9 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -239,13 +239,10 @@ static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { } // Critical Native -static jboolean Font_isSameBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong lFontHandle, - jlong rFontHandle) { - FontWrapper* lFont = reinterpret_cast(lFontHandle); - FontWrapper* rFont = reinterpret_cast(rFontHandle); - const void* lBufferPtr = lFont->font->typeface()->GetFontData(); - const void* rBufferPtr = rFont->font->typeface()->GetFontData(); - return lBufferPtr == rBufferPtr; +static jlong Font_GetBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { + FontWrapper* font = reinterpret_cast(fontHandle); + const void* bufferPtr = font->font->typeface()->GetFontData(); + return reinterpret_cast(bufferPtr); } /////////////////////////////////////////////////////////////////////////////// @@ -297,7 +294,7 @@ static const JNINativeMethod gFontMethods[] = { { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo }, { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath }, { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr }, - { "nIsSameBufferAddress", "(JJ)Z", (void*) Font_isSameBufferAddress }, + { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress }, }; static const JNINativeMethod gFontBufferHelperMethods[] = {