Merge "Update native font API to read updated font files." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2ce8aef9ee
@@ -20,6 +20,7 @@ import android.graphics.Typeface;
|
||||
import android.os.SharedMemory;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
@@ -55,9 +56,10 @@ public class TypefaceSerializationPerfTest {
|
||||
ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
ArrayMap<String, Typeface> out = new ArrayMap<>();
|
||||
while (state.keepRunning()) {
|
||||
buffer.position(0);
|
||||
Typeface.deserializeFontMap(buffer);
|
||||
Typeface.deserializeFontMap(buffer, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -999,7 +999,7 @@ package android.graphics {
|
||||
}
|
||||
|
||||
public class Typeface {
|
||||
method @NonNull public static java.util.Map<java.lang.String,android.graphics.Typeface> deserializeFontMap(@NonNull java.nio.ByteBuffer) throws java.io.IOException;
|
||||
method @NonNull public static long[] deserializeFontMap(@NonNull java.nio.ByteBuffer, @NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws java.io.IOException;
|
||||
method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
|
||||
method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.graphics.fonts.FontFamily;
|
||||
import android.graphics.fonts.SystemFonts;
|
||||
import android.os.SharedMemory;
|
||||
import android.text.FontConfig;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
@@ -200,8 +201,9 @@ public class TypefaceTest {
|
||||
Map<String, Typeface> systemFontMap = SystemFonts.buildSystemTypefaces(fontConfig,
|
||||
fallbackMap);
|
||||
SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap);
|
||||
Map<String, Typeface> copiedFontMap =
|
||||
Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN));
|
||||
Map<String, Typeface> copiedFontMap = new ArrayMap<>();
|
||||
Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN),
|
||||
copiedFontMap);
|
||||
assertEquals(systemFontMap.size(), copiedFontMap.size());
|
||||
for (String key : systemFontMap.keySet()) {
|
||||
assertTrue(copiedFontMap.containsKey(key));
|
||||
|
||||
@@ -1259,20 +1259,21 @@ public class Typeface {
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static @NonNull Map<String, Typeface> deserializeFontMap(@NonNull ByteBuffer buffer)
|
||||
public static @NonNull long[] deserializeFontMap(
|
||||
@NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out)
|
||||
throws IOException {
|
||||
Map<String, Typeface> fontMap = new ArrayMap<>();
|
||||
int typefacesBytesCount = buffer.getInt();
|
||||
long[] nativePtrs = nativeReadTypefaces(buffer.slice());
|
||||
if (nativePtrs == null) {
|
||||
throw new IOException("Could not read typefaces");
|
||||
}
|
||||
out.clear();
|
||||
buffer.position(buffer.position() + typefacesBytesCount);
|
||||
for (long nativePtr : nativePtrs) {
|
||||
String name = readString(buffer);
|
||||
fontMap.put(name, new Typeface(nativePtr));
|
||||
out.put(name, new Typeface(nativePtr));
|
||||
}
|
||||
return fontMap;
|
||||
return nativePtrs;
|
||||
}
|
||||
|
||||
private static String readString(ByteBuffer buffer) {
|
||||
@@ -1330,7 +1331,14 @@ public class Typeface {
|
||||
return;
|
||||
}
|
||||
sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
|
||||
Map<String, Typeface> systemFontMap = deserializeFontMap(sSystemFontMapBuffer);
|
||||
Map<String, Typeface> systemFontMap = new ArrayMap<>();
|
||||
long[] nativePtrs = deserializeFontMap(sSystemFontMapBuffer, systemFontMap);
|
||||
|
||||
// Initialize native font APIs. The native font API will read fonts.xml by itself if
|
||||
// Typeface is initialized with loadPreinstalledSystemFontMap.
|
||||
for (long ptr : nativePtrs) {
|
||||
nativeAddFontCollections(ptr);
|
||||
}
|
||||
setSystemFontMap(systemFontMap);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
|
||||
@@ -1528,5 +1536,8 @@ public class Typeface {
|
||||
|
||||
private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
|
||||
|
||||
@CriticalNative
|
||||
private static native void nativeAddFontCollections(long nativePtr);
|
||||
|
||||
private static native void nativeWarmUpCache(String fileName);
|
||||
}
|
||||
|
||||
@@ -373,6 +373,12 @@ static void Typeface_warmUpCache(JNIEnv* env, jobject, jstring jFilePath) {
|
||||
makeSkDataCached(filePath.c_str(), false /* fs verity */);
|
||||
}
|
||||
|
||||
// Critical Native
|
||||
static void Typeface_addFontCollection(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
|
||||
std::shared_ptr<minikin::FontCollection> collection = toTypeface(faceHandle)->fFontCollection;
|
||||
minikin::SystemFonts::addFontMap(std::move(collection));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const JNINativeMethod gTypefaceMethods[] = {
|
||||
@@ -397,6 +403,7 @@ static const JNINativeMethod gTypefaceMethods[] = {
|
||||
{"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
|
||||
{"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
|
||||
{"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
|
||||
{"nativeAddFontCollections", "(J)V", (void*)Typeface_addFontCollection},
|
||||
};
|
||||
|
||||
int register_android_graphics_Typeface(JNIEnv* env)
|
||||
|
||||
@@ -54,21 +54,51 @@ struct ParserState {
|
||||
XmlCharUniquePtr mLocale;
|
||||
};
|
||||
|
||||
struct ASystemFontIterator {
|
||||
XmlDocUniquePtr mXmlDoc;
|
||||
ParserState state;
|
||||
|
||||
// The OEM customization XML.
|
||||
XmlDocUniquePtr mCustomizationXmlDoc;
|
||||
};
|
||||
|
||||
struct AFont {
|
||||
std::string mFilePath;
|
||||
std::unique_ptr<std::string> mLocale;
|
||||
std::optional<std::string> mLocale;
|
||||
uint16_t mWeight;
|
||||
bool mItalic;
|
||||
uint32_t mCollectionIndex;
|
||||
std::vector<std::pair<uint32_t, float>> mAxes;
|
||||
|
||||
bool operator==(const AFont& o) const {
|
||||
return mFilePath == o.mFilePath && mLocale == o.mLocale && mWeight == o.mWeight &&
|
||||
mItalic == o.mItalic && mCollectionIndex == o.mCollectionIndex && mAxes == o.mAxes;
|
||||
}
|
||||
|
||||
AFont() = default;
|
||||
AFont(const AFont&) = default;
|
||||
};
|
||||
|
||||
struct FontHasher {
|
||||
std::size_t operator()(const AFont& font) const {
|
||||
std::size_t r = std::hash<std::string>{}(font.mFilePath);
|
||||
if (font.mLocale) {
|
||||
r = combine(r, std::hash<std::string>{}(*font.mLocale));
|
||||
}
|
||||
r = combine(r, std::hash<uint16_t>{}(font.mWeight));
|
||||
r = combine(r, std::hash<uint32_t>{}(font.mCollectionIndex));
|
||||
for (const auto& [tag, value] : font.mAxes) {
|
||||
r = combine(r, std::hash<uint32_t>{}(tag));
|
||||
r = combine(r, std::hash<float>{}(value));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
std::size_t combine(std::size_t l, std::size_t r) const { return l ^ (r << 1); }
|
||||
};
|
||||
|
||||
struct ASystemFontIterator {
|
||||
std::vector<AFont> fonts;
|
||||
uint32_t index;
|
||||
|
||||
XmlDocUniquePtr mXmlDoc;
|
||||
|
||||
ParserState state;
|
||||
|
||||
// The OEM customization XML.
|
||||
XmlDocUniquePtr mCustomizationXmlDoc;
|
||||
};
|
||||
|
||||
struct AFontMatcher {
|
||||
@@ -147,10 +177,9 @@ void copyFont(const XmlDocUniquePtr& xmlDoc, const ParserState& state, AFont* ou
|
||||
out->mCollectionIndex = indexStr ?
|
||||
strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
|
||||
|
||||
out->mLocale.reset(
|
||||
state.mLocale ?
|
||||
new std::string(reinterpret_cast<const char*>(state.mLocale.get()))
|
||||
: nullptr);
|
||||
if (state.mLocale) {
|
||||
out->mLocale.emplace(reinterpret_cast<const char*>(state.mLocale.get()));
|
||||
}
|
||||
|
||||
const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag");
|
||||
const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
|
||||
@@ -214,8 +243,44 @@ bool findFirstFontNode(const XmlDocUniquePtr& doc, ParserState* state) {
|
||||
|
||||
ASystemFontIterator* ASystemFontIterator_open() {
|
||||
std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
|
||||
ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
|
||||
ite->mCustomizationXmlDoc.reset(xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
|
||||
|
||||
std::unordered_set<AFont, FontHasher> fonts;
|
||||
minikin::SystemFonts::getFontMap(
|
||||
[&fonts](const std::vector<std::shared_ptr<minikin::FontCollection>>& collections) {
|
||||
for (const auto& fc : collections) {
|
||||
for (const auto& family : fc->getFamilies()) {
|
||||
for (uint32_t i = 0; i < family->getNumFonts(); ++i) {
|
||||
const minikin::Font* font = family->getFont(i);
|
||||
|
||||
std::optional<std::string> locale;
|
||||
uint32_t localeId = font->getLocaleListId();
|
||||
if (localeId != minikin::kEmptyLocaleListId) {
|
||||
locale.emplace(minikin::getLocaleString(localeId));
|
||||
}
|
||||
std::vector<std::pair<uint32_t, float>> axes;
|
||||
for (const auto& [tag, value] : font->typeface()->GetAxes()) {
|
||||
axes.push_back(std::make_pair(tag, value));
|
||||
}
|
||||
|
||||
fonts.insert(
|
||||
{font->typeface()->GetFontPath(), std::move(locale),
|
||||
font->style().weight(),
|
||||
font->style().slant() == minikin::FontStyle::Slant::ITALIC,
|
||||
static_cast<uint32_t>(font->typeface()->GetFontIndex()),
|
||||
axes});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (fonts.empty()) {
|
||||
ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
|
||||
ite->mCustomizationXmlDoc.reset(
|
||||
xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
|
||||
} else {
|
||||
ite->index = 0;
|
||||
ite->fonts.assign(fonts.begin(), fonts.end());
|
||||
}
|
||||
return ite.release();
|
||||
}
|
||||
|
||||
@@ -308,6 +373,13 @@ bool findNextFontNode(const XmlDocUniquePtr& xmlDoc, ParserState* state) {
|
||||
|
||||
AFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
|
||||
LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
|
||||
if (!ite->fonts.empty()) {
|
||||
if (ite->index >= ite->fonts.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new AFont(ite->fonts[ite->index++]);
|
||||
}
|
||||
|
||||
if (ite->mXmlDoc) {
|
||||
if (!findNextFontNode(ite->mXmlDoc, &ite->state)) {
|
||||
// Reached end of the XML file. Continue OEM customization.
|
||||
|
||||
Reference in New Issue
Block a user