diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index e7c15821e5b33..fea756c8290fd 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -123,6 +123,19 @@ public final class Font { mLocaleList = localeList; } + /** + * Construct a builder with a byte buffer and file path. + * + * This method is intended to be called only from SystemFonts. + * @param path font file path + * @param localeList comma concatenated BCP47 compliant language tag. + * @hide + */ + public Builder(@NonNull File path, @NonNull String localeList) { + this(path); + mLocaleList = localeList; + } + /** * Constructs a builder with a file path. * @@ -809,29 +822,13 @@ public final class Font { // If not found, create Font object from native object for Java API users. ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr); - long packed = nGetFontInfo(ptr); - int weight = (int) (packed & 0x0000_0000_0000_FFFFL); - boolean italic = (packed & 0x0000_0000_0001_0000L) != 0; - int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32); - int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48); - FontVariationAxis[] axes = new FontVariationAxis[axisCount]; - char[] charBuffer = new char[4]; - for (int i = 0; i < axisCount; ++i) { - long packedAxis = nGetAxisInfo(ptr, i); - float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL)); - charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56); - charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48); - charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40); - charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32); - axes[i] = new FontVariationAxis(new String(charBuffer), value); - } - String path = nGetFontPath(ptr); - File file = (path == null) ? null : new File(path); - Font.Builder builder = new Font.Builder(buffer, file, "") - .setWeight(weight) - .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT) - .setTtcIndex(ttcIndex) - .setFontVariationSettings(axes); + NativeFont.Font font = NativeFont.readNativeFont(ptr); + + Font.Builder builder = new Font.Builder(buffer, font.getFile(), "") + .setWeight(font.getStyle().getWeight()) + .setSlant(font.getStyle().getSlant()) + .setTtcIndex(font.getIndex()) + .setFontVariationSettings(font.getAxes()); Font newFont = null; try { @@ -845,15 +842,6 @@ public final class Font { } } - @CriticalNative - private static native long nGetFontInfo(long ptr); - - @CriticalNative - private static native long nGetAxisInfo(long ptr, int i); - - @FastNative - private static native String nGetFontPath(long ptr); - @FastNative private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect); diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java new file mode 100644 index 0000000000000..9e9d76a89385d --- /dev/null +++ b/graphics/java/android/graphics/fonts/NativeFont.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.fonts; + +import android.graphics.Typeface; + +import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Read native font objects. + * + * @hide + */ +public class NativeFont { + + /** + * Represents native font object. + */ + public static final class Font { + private final File mFile; + private final int mIndex; + private final FontVariationAxis[] mAxes; + private final FontStyle mStyle; + + public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) { + mFile = file; + mIndex = index; + mAxes = axes; + mStyle = style; + } + + public File getFile() { + return mFile; + } + + public FontVariationAxis[] getAxes() { + return mAxes; + } + + public FontStyle getStyle() { + return mStyle; + } + + public int getIndex() { + return mIndex; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Font font = (Font) o; + return mIndex == font.mIndex && mFile.equals(font.mFile) + && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle); + } + + @Override + public int hashCode() { + int result = Objects.hash(mFile, mIndex, mStyle); + result = 31 * result + Arrays.hashCode(mAxes); + return result; + } + } + + /** + * Represents native font family object. + */ + public static final class Family { + private final List mFonts; + private final String mLocale; + + public Family(List fonts, String locale) { + mFonts = fonts; + mLocale = locale; + } + + public List getFonts() { + return mFonts; + } + + public String getLocale() { + return mLocale; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Family family = (Family) o; + return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale); + } + + @Override + public int hashCode() { + return Objects.hash(mFonts, mLocale); + } + } + + /** + * Get underlying font families from Typeface + * + * @param typeface a typeface + * @return list of family + */ + public static List readTypeface(Typeface typeface) { + int familyCount = nGetFamilyCount(typeface.native_instance); + List result = new ArrayList<>(familyCount); + for (int i = 0; i < familyCount; ++i) { + result.add(readNativeFamily(nGetFamily(typeface.native_instance, i))); + } + return result; + } + + /** + * Read family object from native pointer + * + * @param familyPtr a font family pointer + * @return a family + */ + public static Family readNativeFamily(long familyPtr) { + int fontCount = nGetFontCount(familyPtr); + List result = new ArrayList<>(fontCount); + for (int i = 0; i < fontCount; ++i) { + result.add(readNativeFont(nGetFont(familyPtr, i))); + } + String localeList = nGetLocaleList(familyPtr); + return new Family(result, localeList); + } + + /** + * Read font object from native pointer. + * + * @param ptr a font pointer + * @return a font + */ + public static Font readNativeFont(long ptr) { + long packed = nGetFontInfo(ptr); + int weight = (int) (packed & 0x0000_0000_0000_FFFFL); + boolean italic = (packed & 0x0000_0000_0001_0000L) != 0; + int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32); + int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48); + FontVariationAxis[] axes = new FontVariationAxis[axisCount]; + char[] charBuffer = new char[4]; + for (int i = 0; i < axisCount; ++i) { + long packedAxis = nGetAxisInfo(ptr, i); + float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL)); + charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56); + charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48); + charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40); + charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32); + axes[i] = new FontVariationAxis(new String(charBuffer), value); + } + String path = nGetFontPath(ptr); + File file = (path == null) ? null : new File(path); + FontStyle style = new FontStyle(weight, + italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT); + + return new Font(file, ttcIndex, axes, style); + } + + @CriticalNative + private static native int nGetFamilyCount(long ptr); + + @CriticalNative + private static native long nGetFamily(long ptr, int index); + + @FastNative + private static native String nGetLocaleList(long familyPtr); + + @CriticalNative + private static native long nGetFont(long familyPtr, int fontIndex); + + @CriticalNative + private static native int nGetFontCount(long familyPtr); + + @CriticalNative + private static native long nGetFontInfo(long fontPtr); + + @CriticalNative + private static native long nGetAxisInfo(long fontPtr, int i); + + @FastNative + private static native String nGetFontPath(long fontPtr); +} diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 3635adc3ce39c..28943baf95035 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -19,6 +19,7 @@ package android.graphics.fonts; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; +import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; @@ -68,19 +69,50 @@ public final class SystemFonts { return sAvailableFonts; } - Set set = new HashSet<>(); + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + sAvailableFonts = collectAllFonts(); + } else { + Set set = new HashSet<>(); + for (FontFamily[] items : sFamilyMap.values()) { + for (FontFamily family : items) { + for (int i = 0; i < family.getSize(); ++i) { + set.add(family.getFont(i)); + } + } + } - for (FontFamily[] items : sFamilyMap.values()) { - for (FontFamily family : items) { - for (int i = 0; i < family.getSize(); ++i) { - set.add(family.getFont(i)); + sAvailableFonts = Collections.unmodifiableSet(set); + } + return sAvailableFonts; + } + } + + private static @NonNull Set collectAllFonts() { + Map map = Typeface.getSystemFontMap(); + HashSet seenFonts = new HashSet<>(); + HashSet result = new HashSet<>(); + for (Typeface typeface : map.values()) { + List families = NativeFont.readTypeface(typeface); + for (NativeFont.Family family : families) { + for (NativeFont.Font font : family.getFonts()) { + if (seenFonts.contains(font)) { + continue; + } + seenFonts.add(font); + try { + result.add(new Font.Builder(font.getFile(), family.getLocale()) + .setFontVariationSettings(font.getAxes()) + .setTtcIndex(font.getIndex()) + .setWeight(font.getStyle().getWeight()) + .setSlant(font.getStyle().getSlant()) + .build()); + } catch (IOException e) { + Log.w(TAG, "Failed to load " + font.getFile(), e); } } } - - sAvailableFonts = Collections.unmodifiableSet(set); - return sAvailableFonts; } + return result; } private static @Nullable ByteBuffer mmap(@NonNull String fullPath) { diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index cd53217d29245..1ff1978044b94 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -333,6 +333,7 @@ cc_defaults { "jni/YuvToJpegEncoder.cpp", "jni/fonts/Font.cpp", "jni/fonts/FontFamily.cpp", + "jni/fonts/NativeFont.cpp", "jni/text/LineBreaker.cpp", "jni/text/MeasuredText.cpp", "jni/text/TextShaper.cpp", diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index e1f5abd786bf0..0fad2d58cc8ac 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -69,6 +69,7 @@ extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_fonts_NativeFont(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); @@ -135,6 +136,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_drawable_VectorDrawable), REG_JNI(register_android_graphics_fonts_Font), REG_JNI(register_android_graphics_fonts_FontFamily), + REG_JNI(register_android_graphics_fonts_NativeFont), REG_JNI(register_android_graphics_pdf_PdfDocument), REG_JNI(register_android_graphics_pdf_PdfEditor), REG_JNI(register_android_graphics_pdf_PdfRenderer), diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index f0c77930cbe3a..f612bce748ff1 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -186,38 +186,6 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong return spacing; } -// Critical Native -static jlong Font_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { - const minikin::Font* font = reinterpret_cast(fontHandle); - MinikinFontSkia* minikinSkia = static_cast(font->typeface().get()); - - uint64_t result = font->style().weight(); - result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000; - result |= ((static_cast(minikinSkia->GetFontIndex())) << 32); - result |= ((static_cast(minikinSkia->GetAxes().size())) << 48); - return result; -} - -// Critical Native -static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) { - const minikin::Font* font = reinterpret_cast(fontHandle); - MinikinFontSkia* minikinSkia = static_cast(font->typeface().get()); - const minikin::FontVariation& var = minikinSkia->GetAxes().at(index); - uint32_t floatBinary = *reinterpret_cast(&var.value); - return (static_cast(var.axisTag) << 32) | static_cast(floatBinary); -} - -// FastNative -static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { - const minikin::Font* font = reinterpret_cast(fontHandle); - MinikinFontSkia* minikinSkia = static_cast(font->typeface().get()); - const std::string& filePath = minikinSkia->getFilePath(); - if (filePath.empty()) { - return nullptr; - } - return env->NewStringUTF(filePath.c_str()); -} - // Critical Native static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { FontWrapper* font = reinterpret_cast(fontHandle); @@ -276,9 +244,6 @@ static const JNINativeMethod gFontBuilderMethods[] = { static const JNINativeMethod gFontMethods[] = { { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds }, { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics }, - { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo }, - { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo }, - { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath }, { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr }, { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress }, }; diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp new file mode 100644 index 0000000000000..c5c5d464ccac4 --- /dev/null +++ b/libs/hwui/jni/fonts/NativeFont.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "Minikin" + +#include "Font.h" +#include "SkData.h" +#include "SkFont.h" +#include "SkFontMetrics.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include +#include "Utils.h" +#include "FontUtils.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace android { + +// Critical Native +static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) { + Typeface* tf = reinterpret_cast(typefaceHandle); + return tf->fFontCollection->getFamilies().size(); +} + +// Critical Native +static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) { + Typeface* tf = reinterpret_cast(typefaceHandle); + return reinterpret_cast(tf->fFontCollection->getFamilies()[index].get()); + +} + +// Fast Native +static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) { + minikin::FontFamily* family = reinterpret_cast(familyHandle); + uint32_t localeListId = family->localeListId(); + return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str()); +} + +// Critical Native +static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) { + minikin::FontFamily* family = reinterpret_cast(familyHandle); + return family->getNumFonts(); +} + +// Critical Native +static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) { + minikin::FontFamily* family = reinterpret_cast(familyHandle); + return reinterpret_cast(family->getFont(index)); +} + +// Critical Native +static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { + const minikin::Font* font = reinterpret_cast(fontHandle); + MinikinFontSkia* minikinSkia = static_cast(font->typeface().get()); + + uint64_t result = font->style().weight(); + result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000; + result |= ((static_cast(minikinSkia->GetFontIndex())) << 32); + result |= ((static_cast(minikinSkia->GetAxes().size())) << 48); + return result; +} + +// Critical Native +static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) { + const minikin::Font* font = reinterpret_cast(fontHandle); + MinikinFontSkia* minikinSkia = static_cast(font->typeface().get()); + const minikin::FontVariation& var = minikinSkia->GetAxes().at(index); + uint32_t floatBinary = *reinterpret_cast(&var.value); + return (static_cast(var.axisTag) << 32) | static_cast(floatBinary); +} + +// FastNative +static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { + const minikin::Font* font = reinterpret_cast(fontHandle); + MinikinFontSkia* minikinSkia = static_cast(font->typeface().get()); + const std::string& filePath = minikinSkia->getFilePath(); + if (filePath.empty()) { + return nullptr; + } + return env->NewStringUTF(filePath.c_str()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gNativeFontMethods[] = { + { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount }, + { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily }, + { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList }, + { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount }, + { "nGetFont", "(JI)J", (void*) NativeFont_getFont }, + { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo }, + { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo }, + { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath }, +}; + +int register_android_graphics_fonts_NativeFont(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods, + NELEM(gNativeFontMethods)); +} + +} // namespace android