Merge "Improve Paint.measureText and Paint.hasGlyph for variation sequences." into nyc-dev
This commit is contained in:
@@ -23,7 +23,8 @@ namespace android {
|
||||
|
||||
class CmapCoverage {
|
||||
public:
|
||||
static bool getCoverage(SparseBitSet &coverage, const uint8_t* cmap_data, size_t cmap_size);
|
||||
static bool getCoverage(SparseBitSet &coverage, const uint8_t* cmap_data, size_t cmap_size,
|
||||
bool* has_cmap_format14_subtable);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -89,6 +89,9 @@ private:
|
||||
// This vector contains pointers into mInstances
|
||||
std::vector<FontFamily*> mFamilyVec;
|
||||
|
||||
// This vector has pointers to the font family instance which has cmap 14 subtable.
|
||||
std::vector<FontFamily*> mVSFamilyVec;
|
||||
|
||||
// These are offsets into mInstanceVec, one range per page
|
||||
std::vector<Range> mRanges;
|
||||
};
|
||||
|
||||
@@ -104,7 +104,11 @@ public:
|
||||
|
||||
FontFamily(int variant);
|
||||
|
||||
FontFamily(uint32_t langId, int variant) : mLangId(langId), mVariant(variant) {
|
||||
FontFamily(uint32_t langId, int variant)
|
||||
: mLangId(langId),
|
||||
mVariant(variant),
|
||||
mHasVSTable(false),
|
||||
mCoverageValid(false) {
|
||||
}
|
||||
|
||||
~FontFamily();
|
||||
@@ -131,6 +135,9 @@ public:
|
||||
// Caller should acquire a lock before calling the method.
|
||||
bool hasVariationSelector(uint32_t codepoint, uint32_t variationSelector);
|
||||
|
||||
// Returns true if this font family has a variaion sequence table (cmap format 14 subtable).
|
||||
bool hasVSTable() const;
|
||||
|
||||
private:
|
||||
void addFontLocked(MinikinFont* typeface, FontStyle style);
|
||||
|
||||
@@ -146,6 +153,7 @@ private:
|
||||
std::vector<Font> mFonts;
|
||||
|
||||
SparseBitSet mCoverage;
|
||||
bool mHasVSTable;
|
||||
bool mCoverageValid;
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +131,8 @@ static bool getCoverageFormat12(vector<uint32_t>& coverage, const uint8_t* data,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data, size_t cmap_size) {
|
||||
bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data, size_t cmap_size,
|
||||
bool* has_cmap_format14_subtable) {
|
||||
vector<uint32_t> coverageVec;
|
||||
const size_t kHeaderSize = 4;
|
||||
const size_t kNumTablesOffset = 2;
|
||||
@@ -139,8 +140,10 @@ bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data,
|
||||
const size_t kPlatformIdOffset = 0;
|
||||
const size_t kEncodingIdOffset = 2;
|
||||
const size_t kOffsetOffset = 4;
|
||||
const uint16_t kUnicodePlatformId = 0;
|
||||
const uint16_t kMicrosoftPlatformId = 3;
|
||||
const uint16_t kUnicodeBmpEncodingId = 1;
|
||||
const uint16_t kVariationSequencesEncodingId = 5;
|
||||
const uint16_t kUnicodeUcs4EncodingId = 10;
|
||||
const uint32_t kNoTable = UINT32_MAX;
|
||||
if (kHeaderSize > cmap_size) {
|
||||
@@ -151,6 +154,7 @@ bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data,
|
||||
return false;
|
||||
}
|
||||
uint32_t bestTable = kNoTable;
|
||||
bool hasCmapFormat14Subtable = false;
|
||||
for (uint32_t i = 0; i < numTables; i++) {
|
||||
uint16_t platformId = readU16(cmap_data, kHeaderSize + i * kTableSize + kPlatformIdOffset);
|
||||
uint16_t encodingId = readU16(cmap_data, kHeaderSize + i * kTableSize + kEncodingIdOffset);
|
||||
@@ -159,8 +163,15 @@ bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data,
|
||||
break;
|
||||
} else if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeBmpEncodingId) {
|
||||
bestTable = i;
|
||||
} else if (platformId == kUnicodePlatformId &&
|
||||
encodingId == kVariationSequencesEncodingId) {
|
||||
uint32_t offset = readU32(cmap_data, kHeaderSize + i * kTableSize + kOffsetOffset);
|
||||
if (offset <= cmap_size - 2 && readU16(cmap_data, offset) == 14) {
|
||||
hasCmapFormat14Subtable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
*has_cmap_format14_subtable = hasCmapFormat14Subtable;
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("best table = %d\n", bestTable);
|
||||
#endif
|
||||
|
||||
@@ -62,6 +62,9 @@ FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
|
||||
continue;
|
||||
}
|
||||
mFamilies.push_back(family); // emplace_back would be better
|
||||
if (family->hasVSTable()) {
|
||||
mVSFamilyVec.push_back(family);
|
||||
}
|
||||
mMaxChar = max(mMaxChar, coverage->length());
|
||||
lastChar.push_back(coverage->nextSetBit(0));
|
||||
}
|
||||
@@ -233,15 +236,22 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Even if the font supports variation sequence, mRanges isn't aware of the base character of
|
||||
// the sequence. Search all FontFamilies if variation sequence is specified.
|
||||
// TODO: Always use mRanges for font search.
|
||||
const std::vector<FontFamily*>& familyVec = (vs == 0) ? mFamilyVec : mFamilies;
|
||||
Range range;
|
||||
if (vs == 0) {
|
||||
range = mRanges[ch >> kLogCharsPerPage];
|
||||
} else {
|
||||
range = { 0, mFamilies.size() };
|
||||
const std::vector<FontFamily*>* familyVec = &mFamilyVec;
|
||||
Range range = mRanges[ch >> kLogCharsPerPage];
|
||||
|
||||
std::vector<FontFamily*> familyVecForVS;
|
||||
if (vs != 0) {
|
||||
// If variation selector is specified, need to search for both the variation sequence and
|
||||
// its base codepoint. Compute the union vector of them.
|
||||
familyVecForVS = mVSFamilyVec;
|
||||
familyVecForVS.insert(familyVecForVS.end(),
|
||||
mFamilyVec.begin() + range.start, mFamilyVec.begin() + range.end);
|
||||
std::sort(familyVecForVS.begin(), familyVecForVS.end());
|
||||
auto last = std::unique(familyVecForVS.begin(), familyVecForVS.end());
|
||||
familyVecForVS.erase(last, familyVecForVS.end());
|
||||
|
||||
familyVec = &familyVecForVS;
|
||||
range = { 0, familyVecForVS.size() };
|
||||
}
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
@@ -250,7 +260,7 @@ FontFamily* FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
|
||||
FontFamily* bestFamily = nullptr;
|
||||
uint32_t bestScore = kUnsupportedFontScore;
|
||||
for (size_t i = range.start; i < range.end; i++) {
|
||||
FontFamily* family = familyVec[i];
|
||||
FontFamily* family = (*familyVec)[i];
|
||||
const uint32_t score = calcFamilyScore(ch, vs, variant, langListId, family);
|
||||
if (score == kFirstFontScore) {
|
||||
// If the first font family supports the given character or variation sequence, always
|
||||
@@ -310,12 +320,15 @@ bool FontCollection::hasVariationSelector(uint32_t baseCodepoint,
|
||||
if (baseCodepoint >= mMaxChar) {
|
||||
return false;
|
||||
}
|
||||
if (variationSelector == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Currently mRanges can not be used here since it isn't aware of the variation sequence.
|
||||
// TODO: Use mRanges for narrowing down the search range.
|
||||
for (size_t i = 0; i < mFamilies.size(); i++) {
|
||||
for (size_t i = 0; i < mVSFamilyVec.size(); i++) {
|
||||
AutoMutex _l(gMinikinLock);
|
||||
if (mFamilies[i]->hasVariationSelector(baseCodepoint, variationSelector)) {
|
||||
return true;
|
||||
if (mVSFamilyVec[i]->hasVariationSelector(baseCodepoint, variationSelector)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -177,7 +177,8 @@ const SparseBitSet* FontFamily::getCoverage() {
|
||||
ALOGE("Unexpected failure to read cmap table!\n");
|
||||
return nullptr;
|
||||
}
|
||||
CmapCoverage::getCoverage(mCoverage, cmapData.get(), cmapSize); // TODO: Error check?
|
||||
// TODO: Error check?
|
||||
CmapCoverage::getCoverage(mCoverage, cmapData.get(), cmapSize, &mHasVSTable);
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("font coverage length=%d, first ch=%x\n", mCoverage.length(),
|
||||
mCoverage.nextSetBit(0));
|
||||
@@ -189,6 +190,10 @@ const SparseBitSet* FontFamily::getCoverage() {
|
||||
|
||||
bool FontFamily::hasVariationSelector(uint32_t codepoint, uint32_t variationSelector) {
|
||||
assertMinikinLocked();
|
||||
if (!mHasVSTable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const FontStyle defaultStyle;
|
||||
MinikinFont* minikinFont = getClosestMatch(defaultStyle).font;
|
||||
hb_font_t* font = getHbFontLocked(minikinFont);
|
||||
@@ -196,4 +201,9 @@ bool FontFamily::hasVariationSelector(uint32_t codepoint, uint32_t variationSele
|
||||
return hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
|
||||
}
|
||||
|
||||
bool FontFamily::hasVSTable() const {
|
||||
LOG_ALWAYS_FATAL_IF(!mCoverageValid, "Do not call this method before getCoverage() call");
|
||||
return mHasVSTable;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -378,4 +378,31 @@ TEST_F(FontFamilyTest, hasVariationSelectorTest) {
|
||||
expectVSGlyphs(&family, kNotSupportedChar, std::set<uint32_t>());
|
||||
}
|
||||
|
||||
TEST_F(FontFamilyTest, hasVSTableTest) {
|
||||
struct TestCase {
|
||||
const std::string fontPath;
|
||||
bool hasVSTable;
|
||||
} testCases[] = {
|
||||
{ kTestFontDir "Ja.ttf", true },
|
||||
{ kTestFontDir "ZhHant.ttf", true },
|
||||
{ kTestFontDir "ZhHans.ttf", true },
|
||||
{ kTestFontDir "Italic.ttf", false },
|
||||
{ kTestFontDir "Bold.ttf", false },
|
||||
{ kTestFontDir "BoldItalic.ttf", false },
|
||||
};
|
||||
|
||||
for (auto testCase : testCases) {
|
||||
SCOPED_TRACE(testCase.hasVSTable ?
|
||||
"Font " + testCase.fontPath + " should have a variation sequence table." :
|
||||
"Font " + testCase.fontPath + " shouldn't have a variation sequence table.");
|
||||
|
||||
MinikinFontForTest minikinFont(testCase.fontPath);
|
||||
FontFamily family;
|
||||
family.addFont(&minikinFont);
|
||||
family.getCoverage();
|
||||
|
||||
EXPECT_EQ(testCase.hasVSTable, family.hasVSTable());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
Reference in New Issue
Block a user