Merge "Make SparseBitSet memory mappable."
diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h
index 00f8486..f002cd6 100644
--- a/include/minikin/FontFamily.h
+++ b/include/minikin/FontFamily.h
@@ -47,13 +47,12 @@
FamilyVariant variant() const { return mVariant; }
// API's for enumerating the fonts in a family. These don't guarantee any particular order
- size_t getNumFonts() const { return mFontsCount; }
+ size_t getNumFonts() const { return mFonts.size(); }
const Font* getFont(size_t index) const { return mFonts[index].get(); }
const std::shared_ptr<Font>& getFontRef(size_t index) const { return mFonts[index]; }
FontStyle getStyle(size_t index) const { return mFonts[index]->style(); }
bool isColorEmojiFamily() const { return mIsColorEmoji; }
- size_t getSupportedAxesCount() const { return mSupportedAxesCount; }
- AxisTag getSupportedAxisAt(size_t index) const { return mSupportedAxes[index]; }
+ const std::unordered_set<AxisTag>& supportedAxes() const { return mSupportedAxes; }
bool isCustomFallback() const { return mIsCustomFallback; }
// Get Unicode coverage.
@@ -64,7 +63,7 @@
bool hasGlyph(uint32_t codepoint, uint32_t variationSelector) const;
// Returns true if this font family has a variaion sequence table (cmap format 14 subtable).
- bool hasVSTable() const { return mCmapFmt14CoverageCount != 0; }
+ bool hasVSTable() const { return !mCmapFmt14Coverage.empty(); }
// Creates new FontFamily based on this family while applying font variations. Returns nullptr
// if none of variations apply to this family.
@@ -73,30 +72,22 @@
private:
FontFamily(uint32_t localeListId, FamilyVariant variant,
- std::unique_ptr<std::shared_ptr<Font>[]>&& fonts, uint32_t fontsCount,
- std::unique_ptr<AxisTag[]>&& supportedAxes, uint32_t supportedAxesCount,
- bool isColorEmoji, bool isCustomFallback, SparseBitSet&& coverage,
- std::unique_ptr<std::unique_ptr<SparseBitSet>[]>&& cmapFmt14Coverage,
- uint16_t cmapFmt14CoverageCount);
+ std::vector<std::shared_ptr<Font>>&& fonts,
+ std::unordered_set<AxisTag>&& supportedAxes, bool isColorEmoji,
+ bool isCustomFallback, SparseBitSet&& coverage,
+ std::vector<std::unique_ptr<SparseBitSet>>&& cmapFmt14Coverage);
void computeCoverage();
-public:
- // Note: to minimize padding, small member fields are grouped at the end.
- std::unique_ptr<std::shared_ptr<Font>[]> mFonts;
- // mSupportedAxes is sorted.
- std::unique_ptr<AxisTag[]> mSupportedAxes;
+ uint32_t mLocaleListId;
+ FamilyVariant mVariant;
+ std::vector<std::shared_ptr<Font>> mFonts;
+ std::unordered_set<AxisTag> mSupportedAxes;
+ bool mIsColorEmoji;
+ bool mIsCustomFallback;
+
SparseBitSet mCoverage;
- std::unique_ptr<std::unique_ptr<SparseBitSet>[]> mCmapFmt14Coverage;
- uint32_t mLocaleListId; // 4 bytes
- uint32_t mFontsCount; // 4 bytes
- // OpenType supports up to 2^16-1 (uint16) axes.
- // https://docs.microsoft.com/en-us/typography/opentype/spec/fvar
- uint16_t mSupportedAxesCount; // 2 bytes
- uint16_t mCmapFmt14CoverageCount; // 2 bytes
- FamilyVariant mVariant; // 1 byte
- bool mIsColorEmoji; // 1 byte
- bool mIsCustomFallback; // 1 byte
+ std::vector<std::unique_ptr<SparseBitSet>> mCmapFmt14Coverage;
MINIKIN_PREVENT_COPY_AND_ASSIGN(FontFamily);
};
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 99242b1..563fb77 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -150,9 +150,8 @@
mMaxChar = max(mMaxChar, coverage.length());
lastChar.push_back(coverage.nextSetBit(0));
- for (size_t i = 0; i < family->getSupportedAxesCount(); i++) {
- mSupportedAxes.insert(family->getSupportedAxisAt(i));
- }
+ const std::unordered_set<AxisTag>& supportedAxes = family->supportedAxes();
+ mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
}
// mMaybeSharedFamilies is not shared.
mMaybeSharedFamilies = families;
diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp
index 7938780..91bcaef 100644
--- a/libs/minikin/FontFamily.cpp
+++ b/libs/minikin/FontFamily.cpp
@@ -43,47 +43,38 @@
FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback)
- : mFonts(std::make_unique<std::shared_ptr<Font>[]>(fonts.size())),
- mLocaleListId(localeListId),
- mFontsCount(static_cast<uint32_t>(fonts.size())),
+ : mLocaleListId(localeListId),
mVariant(variant),
+ mFonts(std::move(fonts)),
mIsColorEmoji(LocaleListCache::getById(localeListId).getEmojiStyle() ==
EmojiStyle::EMOJI),
mIsCustomFallback(isCustomFallback) {
- MINIKIN_ASSERT(!fonts.empty(), "FontFamily must contain at least one font.");
- MINIKIN_ASSERT(fonts.size() <= std::numeric_limits<uint32_t>::max(),
- "Number of fonts must be less than 2^32.");
- for (size_t i = 0; i < mFontsCount; i++) {
- mFonts[i] = std::move(fonts[i]);
- }
+ MINIKIN_ASSERT(!mFonts.empty(), "FontFamily must contain at least one font.");
computeCoverage();
}
FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
- std::unique_ptr<std::shared_ptr<Font>[]>&& fonts, uint32_t fontsCount,
- std::unique_ptr<AxisTag[]>&& supportedAxes, uint32_t supportedAxesCount,
- bool isColorEmoji, bool isCustomFallback, SparseBitSet&& coverage,
- std::unique_ptr<std::unique_ptr<SparseBitSet>[]>&& cmapFmt14Coverage,
- uint16_t cmapFmt14CoverageCount)
- : mFonts(std::move(fonts)),
- mSupportedAxes(std::move(supportedAxes)),
- mCoverage(std::move(coverage)),
- mCmapFmt14Coverage(std::move(cmapFmt14Coverage)),
- mLocaleListId(localeListId),
- mFontsCount(fontsCount),
- mSupportedAxesCount(supportedAxesCount),
- mCmapFmt14CoverageCount(cmapFmt14CoverageCount),
+ std::vector<std::shared_ptr<Font>>&& fonts,
+ std::unordered_set<AxisTag>&& supportedAxes, bool isColorEmoji,
+ bool isCustomFallback, SparseBitSet&& coverage,
+ std::vector<std::unique_ptr<SparseBitSet>>&& cmapFmt14Coverage)
+ : mLocaleListId(localeListId),
mVariant(variant),
+ mFonts(std::move(fonts)),
+ mSupportedAxes(std::move(supportedAxes)),
mIsColorEmoji(isColorEmoji),
- mIsCustomFallback(isCustomFallback) {}
+ mIsCustomFallback(isCustomFallback),
+ mCoverage(std::move(coverage)),
+ mCmapFmt14Coverage(std::move(cmapFmt14Coverage)) {}
// static
std::shared_ptr<FontFamily> FontFamily::readFrom(BufferReader* reader) {
uint32_t localeListId = LocaleListCache::readFrom(reader);
uint32_t fontsCount = reader->read<uint32_t>();
- auto fonts = std::make_unique<std::shared_ptr<Font>[]>(fontsCount);
- for (size_t i = 0; i < fontsCount; i++) {
- fonts[i] = Font::readFrom(reader, localeListId);
+ std::vector<std::shared_ptr<Font>> fonts;
+ fonts.reserve(fontsCount);
+ for (uint32_t i = 0; i < fontsCount; i++) {
+ fonts.emplace_back(Font::readFrom(reader, localeListId));
}
// FamilyVariant is uint8_t
static_assert(sizeof(FamilyVariant) == 1);
@@ -91,48 +82,48 @@
// AxisTag is uint32_t
static_assert(sizeof(AxisTag) == 4);
const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
- auto supportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
- std::copy(axesPtr, axesPtr + axesCount, supportedAxes.get());
+ std::unordered_set<AxisTag> supportedAxes(axesPtr, axesPtr + axesCount);
bool isColorEmoji = static_cast<bool>(reader->read<uint8_t>());
bool isCustomFallback = static_cast<bool>(reader->read<uint8_t>());
SparseBitSet coverage(reader);
// Read mCmapFmt14Coverage. As it can have null entries, it is stored in the buffer as a sparse
// array (size, non-null entry count, array of (index, entry)).
uint32_t cmapFmt14CoverageSize = reader->read<uint32_t>();
- auto cmapFmt14Coverage =
- std::make_unique<std::unique_ptr<SparseBitSet>[]>(cmapFmt14CoverageSize);
+ std::vector<std::unique_ptr<SparseBitSet>> cmapFmt14Coverage(cmapFmt14CoverageSize);
uint32_t cmapFmt14CoverageEntryCount = reader->read<uint32_t>();
for (uint32_t i = 0; i < cmapFmt14CoverageEntryCount; i++) {
uint32_t index = reader->read<uint32_t>();
cmapFmt14Coverage[index] = std::make_unique<SparseBitSet>(reader);
}
return std::shared_ptr<FontFamily>(new FontFamily(
- localeListId, variant, std::move(fonts), fontsCount, std::move(supportedAxes),
- axesCount, isColorEmoji, isCustomFallback, std::move(coverage),
- std::move(cmapFmt14Coverage), cmapFmt14CoverageSize));
+ localeListId, variant, std::move(fonts), std::move(supportedAxes), isColorEmoji,
+ isCustomFallback, std::move(coverage), std::move(cmapFmt14Coverage)));
}
void FontFamily::writeTo(BufferWriter* writer) const {
LocaleListCache::writeTo(writer, mLocaleListId);
- writer->write<uint32_t>(mFontsCount);
- for (size_t i = 0; i < mFontsCount; i++) {
- mFonts[i]->writeTo(writer);
+ writer->write<uint32_t>(mFonts.size());
+ for (const std::shared_ptr<Font>& font : mFonts) {
+ font->writeTo(writer);
}
writer->write<FamilyVariant>(mVariant);
- writer->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
+ std::vector<AxisTag> axes(mSupportedAxes.begin(), mSupportedAxes.end());
+ // Sort axes to be deterministic.
+ std::sort(axes.begin(), axes.end());
+ writer->writeArray<AxisTag>(axes.data(), axes.size());
writer->write<uint8_t>(mIsColorEmoji);
writer->write<uint8_t>(mIsCustomFallback);
mCoverage.writeTo(writer);
// Write mCmapFmt14Coverage as a sparse array (size, non-null entry count,
// array of (index, entry))
- writer->write<uint32_t>(mCmapFmt14CoverageCount);
+ writer->write<uint32_t>(mCmapFmt14Coverage.size());
uint32_t cmapFmt14CoverageEntryCount = 0;
- for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
- if (mCmapFmt14Coverage[i]) cmapFmt14CoverageEntryCount++;
+ for (const std::unique_ptr<SparseBitSet>& coverage : mCmapFmt14Coverage) {
+ if (coverage != nullptr) cmapFmt14CoverageEntryCount++;
}
writer->write<uint32_t>(cmapFmt14CoverageEntryCount);
- for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
- if (mCmapFmt14Coverage[i]) {
+ for (size_t i = 0; i < mCmapFmt14Coverage.size(); i++) {
+ if (mCmapFmt14Coverage[i] != nullptr) {
writer->write<uint32_t>(i);
mCmapFmt14Coverage[i]->writeTo(writer);
}
@@ -163,7 +154,7 @@
int bestIndex = 0;
Font* bestFont = mFonts[bestIndex].get();
int bestMatch = computeMatch(bestFont->style(), style);
- for (size_t i = 1; i < mFontsCount; i++) {
+ for (size_t i = 1; i < mFonts.size(); i++) {
Font* font = mFonts[i].get();
int match = computeMatch(font->style(), style);
if (i == 0 || match < bestMatch) {
@@ -183,30 +174,12 @@
return;
}
- std::vector<std::unique_ptr<SparseBitSet>> cmapFmt14Coverage;
- mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &cmapFmt14Coverage);
- static_assert(INVALID_VS_INDEX <= std::numeric_limits<uint16_t>::max());
- // cmapFmt14Coverage maps VS index to coverage.
- // cmapFmt14Coverage's size cannot exceed INVALID_VS_INDEX.
- MINIKIN_ASSERT(cmapFmt14Coverage.size() <= INVALID_VS_INDEX,
- "cmapFmt14Coverage's size must not exceed INVALID_VS_INDEX.");
- mCmapFmt14CoverageCount = static_cast<uint16_t>(cmapFmt14Coverage.size());
- mCmapFmt14Coverage = std::make_unique<std::unique_ptr<SparseBitSet>[]>(mCmapFmt14CoverageCount);
- for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
- mCmapFmt14Coverage[i] = std::move(cmapFmt14Coverage[i]);
- }
+ mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
- std::unordered_set<AxisTag> supportedAxesSet;
- for (size_t i = 0; i < mFontsCount; ++i) {
+ for (size_t i = 0; i < mFonts.size(); ++i) {
std::unordered_set<AxisTag> supportedAxes = mFonts[i]->getSupportedAxes();
- supportedAxesSet.insert(supportedAxes.begin(), supportedAxes.end());
+ mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
}
- MINIKIN_ASSERT(supportedAxesSet.size() <= std::numeric_limits<uint32_t>::max(),
- "Number of supported axes must be less than 2^16.");
- mSupportedAxesCount = static_cast<uint16_t>(supportedAxesSet.size());
- mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[mSupportedAxesCount]);
- std::copy(supportedAxesSet.begin(), supportedAxesSet.end(), mSupportedAxes.get());
- std::sort(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount);
}
bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
@@ -214,13 +187,13 @@
return mCoverage.get(codepoint);
}
- if (mCmapFmt14CoverageCount == 0) {
+ if (mCmapFmt14Coverage.empty()) {
return false;
}
const uint16_t vsIndex = getVsIndex(variationSelector);
- if (vsIndex >= mCmapFmt14CoverageCount) {
+ if (vsIndex >= mCmapFmt14Coverage.size()) {
// Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
// be at the maximum end of the range.
return false;
@@ -236,14 +209,13 @@
std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
const std::vector<FontVariation>& variations) const {
- if (variations.empty() || mSupportedAxesCount == 0) {
+ if (variations.empty() || mSupportedAxes.empty()) {
return nullptr;
}
bool hasSupportedAxis = false;
for (const FontVariation& variation : variations) {
- if (std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
- variation.axisTag)) {
+ if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
hasSupportedAxis = true;
break;
}
@@ -254,8 +226,7 @@
}
std::vector<std::shared_ptr<Font>> fonts;
- for (size_t i = 0; i < mFontsCount; i++) {
- const std::shared_ptr<Font>& font = mFonts[i];
+ for (const auto& font : mFonts) {
bool supportedVariations = false;
std::unordered_set<AxisTag> supportedAxes = font->getSupportedAxes();
if (!supportedAxes.empty()) {
diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp
index d658cdd..53803fd 100644
--- a/tests/unittest/FontFamilyTest.cpp
+++ b/tests/unittest/FontFamilyTest.cpp
@@ -832,10 +832,7 @@
ASSERT_EQ(original->localeListId(), copied->localeListId());
ASSERT_EQ(original->variant(), copied->variant());
ASSERT_EQ(original->getNumFonts(), copied->getNumFonts());
- ASSERT_EQ(original->getSupportedAxesCount(), copied->getSupportedAxesCount());
- for (size_t i = 0; i < original->getSupportedAxesCount(); i++) {
- ASSERT_EQ(original->getSupportedAxisAt(i), copied->getSupportedAxisAt(i));
- }
+ ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
ASSERT_EQ(original->isColorEmojiFamily(), copied->isColorEmojiFamily());
ASSERT_EQ(original->isCustomFallback(), copied->isCustomFallback());
ASSERT_EQ(original->hasVSTable(), copied->hasVSTable());
@@ -850,10 +847,7 @@
std::vector<uint8_t> buffer = writeToBuffer<FontFamily>(*original);
BufferReader reader(buffer.data());
std::shared_ptr<FontFamily> copied = FontFamily::readFrom(&reader);
- ASSERT_EQ(original->getSupportedAxesCount(), copied->getSupportedAxesCount());
- for (size_t i = 0; i < original->getSupportedAxesCount(); i++) {
- ASSERT_EQ(original->getSupportedAxisAt(i), copied->getSupportedAxisAt(i));
- }
+ ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
std::vector<uint8_t> newBuffer = writeToBuffer<FontFamily>(*copied);
ASSERT_EQ(buffer, newBuffer);
}