Move coverage bitmap from FontCollection to FontFamily

This will significantly reduce memory usage and also speed the creation
of new font families. In particular, the coverage bitmaps for the fonts
in the fallback stack will be computed once in the Zygote, rather than
separately in each app process.

Bug: 17756900
Change-Id: I66f5706bddd4658d78fe5b709f7251ca9d2ff4f8
This commit is contained in:
Raph Levien 2014-10-29 11:04:04 -07:00
parent 92e8cc7071
commit 474b009ef4
4 changed files with 63 additions and 60 deletions

View File

@ -21,7 +21,6 @@
#include <minikin/MinikinRefCounted.h> #include <minikin/MinikinRefCounted.h>
#include <minikin/MinikinFont.h> #include <minikin/MinikinFont.h>
#include <minikin/SparseBitSet.h>
#include <minikin/FontFamily.h> #include <minikin/FontFamily.h>
namespace android { namespace android {
@ -52,17 +51,12 @@ private:
static const int kLogCharsPerPage = 8; static const int kLogCharsPerPage = 8;
static const int kPageMask = (1 << kLogCharsPerPage) - 1; static const int kPageMask = (1 << kLogCharsPerPage) - 1;
struct FontInstance {
SparseBitSet* mCoverage;
FontFamily* mFamily;
};
struct Range { struct Range {
size_t start; size_t start;
size_t end; size_t end;
}; };
const FontInstance* getInstanceForChar(uint32_t ch, FontLanguage lang, int variant) const; FontFamily* getFamilyForChar(uint32_t ch, FontLanguage lang, int variant) const;
// static for allocating unique id's // static for allocating unique id's
static uint32_t sNextId; static uint32_t sNextId;
@ -74,10 +68,10 @@ private:
uint32_t mMaxChar; uint32_t mMaxChar;
// This vector has ownership of the bitsets and typeface objects. // This vector has ownership of the bitsets and typeface objects.
std::vector<FontInstance> mInstances; std::vector<FontFamily*> mFamilies;
// This vector contains pointers into mInstances // This vector contains pointers into mInstances
std::vector<const FontInstance*> mInstanceVec; std::vector<FontFamily*> mFamilyVec;
// These are offsets into mInstanceVec, one range per page // These are offsets into mInstanceVec, one range per page
std::vector<Range> mRanges; std::vector<Range> mRanges;

View File

@ -23,6 +23,7 @@
#include <utils/TypeHelpers.h> #include <utils/TypeHelpers.h>
#include <minikin/MinikinRefCounted.h> #include <minikin/MinikinRefCounted.h>
#include <minikin/SparseBitSet.h>
namespace android { namespace android {
@ -139,6 +140,9 @@ public:
size_t getNumFonts() const; size_t getNumFonts() const;
MinikinFont* getFont(size_t index) const; MinikinFont* getFont(size_t index) const;
FontStyle getStyle(size_t index) const; FontStyle getStyle(size_t index) const;
// Get Unicode coverage. Lifetime of returned bitset is same as receiver.
const SparseBitSet* getCoverage();
private: private:
void addFontLocked(MinikinFont* typeface, FontStyle style); void addFontLocked(MinikinFont* typeface, FontStyle style);
@ -152,6 +156,9 @@ private:
FontLanguage mLang; FontLanguage mLang;
int mVariant; int mVariant;
std::vector<Font> mFonts; std::vector<Font> mFonts;
SparseBitSet mCoverage;
bool mCoverageValid;
}; };
} // namespace android } // namespace android

View File

@ -23,7 +23,6 @@
#include "unicode/unorm2.h" #include "unicode/unorm2.h"
#include "MinikinInternal.h" #include "MinikinInternal.h"
#include <minikin/CmapCoverage.h>
#include <minikin/FontCollection.h> #include <minikin/FontCollection.h>
using std::vector; using std::vector;
@ -54,28 +53,12 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
continue; continue;
} }
family->RefLocked(); family->RefLocked();
FontInstance dummy; mFamilies.push_back(family); // emplace_back would be better
mInstances.push_back(dummy); // emplace_back would be better const SparseBitSet* coverage = family->getCoverage();
FontInstance* instance = &mInstances.back(); mMaxChar = max(mMaxChar, coverage->length());
instance->mFamily = family; lastChar.push_back(coverage->nextSetBit(0));
instance->mCoverage = new SparseBitSet;
#ifdef VERBOSE_DEBUG
ALOGD("closest match = %p, family size = %d\n", typeface, family->getNumFonts());
#endif
const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
size_t cmapSize = 0;
bool ok = typeface->GetTable(cmapTag, NULL, &cmapSize);
UniquePtr<uint8_t[]> cmapData(new uint8_t[cmapSize]);
ok = typeface->GetTable(cmapTag, cmapData.get(), &cmapSize);
CmapCoverage::getCoverage(*instance->mCoverage, cmapData.get(), cmapSize);
#ifdef VERBOSE_DEBUG
ALOGD("font coverage length=%d, first ch=%x\n", instance->mCoverage->length(),
instance->mCoverage->nextSetBit(0));
#endif
mMaxChar = max(mMaxChar, instance->mCoverage->length());
lastChar.push_back(instance->mCoverage->nextSetBit(0));
} }
nTypefaces = mInstances.size(); nTypefaces = mFamilies.size();
LOG_ALWAYS_FATAL_IF(nTypefaces == 0, LOG_ALWAYS_FATAL_IF(nTypefaces == 0,
"Font collection must have at least one valid typeface"); "Font collection must have at least one valid typeface");
size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage; size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage;
@ -90,10 +73,10 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
range->start = offset; range->start = offset;
for (size_t j = 0; j < nTypefaces; j++) { for (size_t j = 0; j < nTypefaces; j++) {
if (lastChar[j] < (i + 1) << kLogCharsPerPage) { if (lastChar[j] < (i + 1) << kLogCharsPerPage) {
const FontInstance* instance = &mInstances[j]; FontFamily* family = mFamilies[j];
mInstanceVec.push_back(instance); mFamilyVec.push_back(family);
offset++; offset++;
uint32_t nextChar = instance->mCoverage->nextSetBit((i + 1) << kLogCharsPerPage); uint32_t nextChar = family->getCoverage()->nextSetBit((i + 1) << kLogCharsPerPage);
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
ALOGD("nextChar = %d (j = %d)\n", nextChar, j); ALOGD("nextChar = %d (j = %d)\n", nextChar, j);
#endif #endif
@ -105,9 +88,8 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
} }
FontCollection::~FontCollection() { FontCollection::~FontCollection() {
for (size_t i = 0; i < mInstances.size(); i++) { for (size_t i = 0; i < mFamilies.size(); i++) {
delete mInstances[i].mCoverage; mFamilies[i]->UnrefLocked();
mInstances[i].mFamily->UnrefLocked();
} }
} }
@ -117,7 +99,7 @@ FontCollection::~FontCollection() {
// 3. If a font matches just language, it gets a score of 2. // 3. If a font matches just language, it gets a score of 2.
// 4. Matching the "compact" or "elegant" variant adds one to the score. // 4. Matching the "compact" or "elegant" variant adds one to the score.
// 5. Highest score wins, with ties resolved to the first font. // 5. Highest score wins, with ties resolved to the first font.
const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t ch, FontFamily* FontCollection::getFamilyForChar(uint32_t ch,
FontLanguage lang, int variant) const { FontLanguage lang, int variant) const {
if (ch >= mMaxChar) { if (ch >= mMaxChar) {
return NULL; return NULL;
@ -126,15 +108,14 @@ const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
ALOGD("querying range %d:%d\n", range.start, range.end); ALOGD("querying range %d:%d\n", range.start, range.end);
#endif #endif
const FontInstance* bestInstance = NULL; FontFamily* bestFamily = NULL;
int bestScore = -1; int bestScore = -1;
for (size_t i = range.start; i < range.end; i++) { for (size_t i = range.start; i < range.end; i++) {
const FontInstance* instance = mInstanceVec[i]; FontFamily* family = mFamilyVec[i];
if (instance->mCoverage->get(ch)) { if (family->getCoverage()->get(ch)) {
FontFamily* family = instance->mFamily;
// First font family in collection always matches // First font family in collection always matches
if (mInstances[0].mFamily == family) { if (mFamilies[0] == family) {
return instance; return family;
} }
int score = lang.match(family->lang()) * 2; int score = lang.match(family->lang()) * 2;
if (variant != 0 && variant == family->variant()) { if (variant != 0 && variant == family->variant()) {
@ -142,11 +123,11 @@ const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t
} }
if (score > bestScore) { if (score > bestScore) {
bestScore = score; bestScore = score;
bestInstance = instance; bestFamily = family;
} }
} }
} }
if (bestInstance == NULL && !mInstanceVec.empty()) { if (bestFamily == NULL && !mFamilyVec.empty()) {
UErrorCode errorCode = U_ZERO_ERROR; UErrorCode errorCode = U_ZERO_ERROR;
const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode); const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode);
if (U_SUCCESS(errorCode)) { if (U_SUCCESS(errorCode)) {
@ -155,12 +136,12 @@ const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t
if (U_SUCCESS(errorCode) && len > 0) { if (U_SUCCESS(errorCode) && len > 0) {
int off = 0; int off = 0;
U16_NEXT_UNSAFE(decomposed, off, ch); U16_NEXT_UNSAFE(decomposed, off, ch);
return getInstanceForChar(ch, lang, variant); return getFamilyForChar(ch, lang, variant);
} }
} }
bestInstance = &mInstances[0]; bestFamily = mFamilies[0];
} }
return bestInstance; return bestFamily;
} }
const uint32_t NBSP = 0xa0; const uint32_t NBSP = 0xa0;
@ -183,7 +164,7 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty
vector<Run>* result) const { vector<Run>* result) const {
FontLanguage lang = style.getLanguage(); FontLanguage lang = style.getLanguage();
int variant = style.getVariant(); int variant = style.getVariant();
const FontInstance* lastInstance = NULL; FontFamily* lastFamily = NULL;
Run* run = NULL; Run* run = NULL;
int nShorts; int nShorts;
for (size_t i = 0; i < string_size; i += nShorts) { for (size_t i = 0; i < string_size; i += nShorts) {
@ -197,17 +178,17 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty
} }
} }
// Continue using existing font as long as it has coverage and is whitelisted // Continue using existing font as long as it has coverage and is whitelisted
if (lastInstance == NULL if (lastFamily == NULL
|| !(isStickyWhitelisted(ch) && lastInstance->mCoverage->get(ch))) { || !(isStickyWhitelisted(ch) && lastFamily->getCoverage()->get(ch))) {
const FontInstance* instance = getInstanceForChar(ch, lang, variant); FontFamily* family = getFamilyForChar(ch, lang, variant);
if (i == 0 || instance != lastInstance) { if (i == 0 || family != lastFamily) {
size_t start = i; size_t start = i;
// Workaround for Emoji keycap until we implement per-cluster font // Workaround for Emoji keycap until we implement per-cluster font
// selection: if keycap is found in a different font that also // selection: if keycap is found in a different font that also
// supports previous char, attach previous char to the new run. // supports previous char, attach previous char to the new run.
// Only handles non-surrogate characters. // Only handles non-surrogate characters.
// Bug 7557244. // Bug 7557244.
if (ch == KEYCAP && i && instance && instance->mCoverage->get(string[i - 1])) { if (ch == KEYCAP && i && family && family->getCoverage()->get(string[i - 1])) {
run->end--; run->end--;
if (run->start == run->end) { if (run->start == run->end) {
result->pop_back(); result->pop_back();
@ -217,12 +198,12 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty
Run dummy; Run dummy;
result->push_back(dummy); result->push_back(dummy);
run = &result->back(); run = &result->back();
if (instance == NULL) { if (family == NULL) {
run->fakedFont.font = NULL; run->fakedFont.font = NULL;
} else { } else {
run->fakedFont = instance->mFamily->getClosestMatch(style); run->fakedFont = family->getClosestMatch(style);
} }
lastInstance = instance; lastFamily = family;
run->start = start; run->start = start;
} }
} }
@ -235,10 +216,10 @@ MinikinFont* FontCollection::baseFont(FontStyle style) {
} }
FakedFont FontCollection::baseFontFaked(FontStyle style) { FakedFont FontCollection::baseFontFaked(FontStyle style) {
if (mInstances.empty()) { if (mFamilies.empty()) {
return FakedFont(); return FakedFont();
} }
return mInstances[0].mFamily->getClosestMatch(style); return mFamilies[0]->getClosestMatch(style);
} }
uint32_t FontCollection::getId() const { uint32_t FontCollection::getId() const {

View File

@ -23,6 +23,7 @@
#include "MinikinInternal.h" #include "MinikinInternal.h"
#include <minikin/MinikinFont.h> #include <minikin/MinikinFont.h>
#include <minikin/AnalyzeStyle.h> #include <minikin/AnalyzeStyle.h>
#include <minikin/CmapCoverage.h>
#include <minikin/FontFamily.h> #include <minikin/FontFamily.h>
#include <UniquePtr.h> #include <UniquePtr.h>
@ -128,6 +129,7 @@ void FontFamily::addFont(MinikinFont* typeface, FontStyle style) {
void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) { typeface->RefLocked(); void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) { typeface->RefLocked();
mFonts.push_back(Font(typeface, style)); mFonts.push_back(Font(typeface, style));
mCoverageValid = false;
} }
// Compute a matching metric between two styles - 0 is an exact match // Compute a matching metric between two styles - 0 is an exact match
@ -183,4 +185,23 @@ FontStyle FontFamily::getStyle(size_t index) const {
return mFonts[index].style; return mFonts[index].style;
} }
const SparseBitSet* FontFamily::getCoverage() {
if (!mCoverageValid) {
const FontStyle defaultStyle;
MinikinFont* typeface = getClosestMatch(defaultStyle).font;
const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
size_t cmapSize = 0;
bool ok = typeface->GetTable(cmapTag, NULL, &cmapSize);
UniquePtr<uint8_t[]> cmapData(new uint8_t[cmapSize]);
ok = typeface->GetTable(cmapTag, cmapData.get(), &cmapSize);
CmapCoverage::getCoverage(mCoverage, cmapData.get(), cmapSize);
#ifdef VERBOSE_DEBUG
ALOGD("font coverage length=%d, first ch=%x\n", mCoverage->length(),
mCoverage->nextSetBit(0));
#endif
mCoverageValid = true;
}
return &mCoverage;
}
} // namespace android } // namespace android