From 1adbccfd0f40ef416e4f76ea16cbc0bae549d669 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Tue, 14 Mar 2017 17:57:28 -0700 Subject: [PATCH] Serialize and deserialize supported axes. To avoid reading font files during FontFamily construction, serialize and deserialize supported axes and cmap coverage at the same time. Bug: 36232655 Test: ran minikin_tests Change-Id: I4086fb887e13f872390b533584bce6f1d5598ea0 --- include/minikin/FontFamily.h | 10 ++++-- libs/minikin/FontFamily.cpp | 57 +++++++++++++++++++++++++------ tests/unittest/FontFamilyTest.cpp | 46 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 14 deletions(-) diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h index 4186c2baabb..18a75ee8ec0 100644 --- a/include/minikin/FontFamily.h +++ b/include/minikin/FontFamily.h @@ -110,10 +110,8 @@ struct Font { std::shared_ptr typeface; FontStyle style; - std::unordered_set supportedAxes; -private: - void loadAxes(); + std::unordered_set getSupportedAxesLocked() const; }; struct FontVariation { @@ -181,6 +179,12 @@ private: void computeCoverage(); void readAcceleratorTable(const uint8_t* data, size_t size); + size_t writeSupportedAxes(uint8_t* out) const; + + // Reads supported axes values from 'in' buffer. This method reads up to + // 'inSize' bytes and returns the number of bytes read. + size_t readSupportedAxes(const uint8_t* in, size_t inSize); + uint32_t mLangId; int mVariant; std::vector mFonts; diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp index 4097e8fb239..076dff2bcc9 100644 --- a/libs/minikin/FontFamily.cpp +++ b/libs/minikin/FontFamily.cpp @@ -67,36 +67,33 @@ uint32_t FontStyle::pack(int variant, int weight, bool italic) { Font::Font(const std::shared_ptr& typeface, FontStyle style) : typeface(typeface), style(style) { - loadAxes(); } Font::Font(std::shared_ptr&& typeface, FontStyle style) : typeface(typeface), style(style) { - loadAxes(); } -void Font::loadAxes() { - android::AutoMutex _l(gMinikinLock); +std::unordered_set Font::getSupportedAxesLocked() const { const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r'); HbBlob fvarTable(getFontTable(typeface.get(), fvarTag)); if (fvarTable.size() == 0) { - return; + return std::unordered_set(); } + std::unordered_set supportedAxes; analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes); + return supportedAxes; } Font::Font(Font&& o) { typeface = std::move(o.typeface); style = o.style; o.typeface = nullptr; - supportedAxes = std::move(o.supportedAxes); } Font::Font(const Font& o) { typeface = o.typeface; style = o.style; - supportedAxes = o.supportedAxes; } // static @@ -201,7 +198,8 @@ void FontFamily::computeCoverage() { CmapCoverage::getCoverage(mCoverage, cmapTable.get(), cmapTable.size(), &mHasVSTable); for (size_t i = 0; i < mFonts.size(); ++i) { - mSupportedAxes.insert(mFonts[i].supportedAxes.begin(), mFonts[i].supportedAxes.end()); + std::unordered_set supportedAxes = mFonts[i].getSupportedAxesLocked(); + mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end()); } } @@ -242,9 +240,11 @@ std::shared_ptr FontFamily::createFamilyWithVariation( std::vector fonts; for (const Font& font : mFonts) { bool supportedVariations = false; - if (!font.supportedAxes.empty()) { + android::AutoMutex _l(gMinikinLock); + std::unordered_set supportedAxes = font.getSupportedAxesLocked(); + if (!supportedAxes.empty()) { for (const FontVariation& variation : variations) { - if (font.supportedAxes.find(variation.axisTag) != font.supportedAxes.end()) { + if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) { supportedVariations = true; break; } @@ -264,11 +264,46 @@ std::shared_ptr FontFamily::createFamilyWithVariation( } size_t FontFamily::writeAcceleratorTable(uint8_t* out) const { - return mCoverage.writeToBuffer(out); + const size_t axesTableSize = writeSupportedAxes(out); + if (out != nullptr) { + out += axesTableSize; + } + const size_t coverageTableSize = mCoverage.writeToBuffer(out); + return axesTableSize + coverageTableSize; } void FontFamily::readAcceleratorTable(const uint8_t* data, size_t size) { + const size_t readSize = readSupportedAxes(data, size); + data += readSize; + size -= readSize; bool result = mCoverage.initFromBuffer(data, size); LOG_ALWAYS_FATAL_IF(!result, "Failed to reconstruct accelerator table from buffer"); } + +size_t FontFamily::writeSupportedAxes(uint8_t* out) const { + LOG_ALWAYS_FATAL_IF(mSupportedAxes.size() > 255, "System fonts may only use up to 255 axes."); + const uint8_t axesCount = static_cast(mSupportedAxes.size()); + if (out != nullptr) { + out[0] = axesCount; + AxisTag* axisTags = reinterpret_cast(out + 1); + for (const auto& tag : mSupportedAxes) { + *axisTags++ = tag; + } + } + const size_t axesSizeInbytes = sizeof(AxisTag) * axesCount; + return axesSizeInbytes + 1 /* 1 for axes count */; +} + +size_t FontFamily::readSupportedAxes(const uint8_t* in, size_t inSize) { + LOG_ALWAYS_FATAL_IF(inSize == 0); + const uint8_t axesCount = in[0]; + const size_t totalSize = sizeof(AxisTag) * axesCount + 1 /* 1 for axes Count */; + LOG_ALWAYS_FATAL_IF(inSize < totalSize); + const AxisTag* axisTags = reinterpret_cast(in + 1); + mSupportedAxes.clear(); + for (uint8_t i = 0; i < axesCount; ++i) { + mSupportedAxes.insert(axisTags[i]); + } + return totalSize; +} } // namespace minikin diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp index 5285f2642b4..58a7bab5f69 100644 --- a/tests/unittest/FontFamilyTest.cpp +++ b/tests/unittest/FontFamilyTest.cpp @@ -656,4 +656,50 @@ TEST_F(FontFamilyTest, createFamilyWithVariationTest) { } } +TEST_F(FontFamilyTest, supportedAxesRestoreFromSerializedBuffer) { + const char kFontPath[] = kTestFontDir "/MultiAxis.ttf"; + + std::shared_ptr font(new MinikinFontForTest(kFontPath)); + std::shared_ptr family = + std::make_shared(std::vector({Font(font, FontStyle())})); + + size_t necessaryBytes = family->writeAcceleratorTable(nullptr); + ASSERT_NE(0U, necessaryBytes); + std::vector buffer; + buffer.resize(necessaryBytes); + family->writeAcceleratorTable(buffer.data()); + + std::shared_ptr restoredMultiAxisFamily = + std::make_shared(std::vector({Font(font, FontStyle())}), + buffer.data(), buffer.size()); + + // This font has 'wdth' and 'wght' axes. + const std::unordered_set& supportedAxes = family->supportedAxes(); + ASSERT_EQ(2U, supportedAxes.size()); + EXPECT_NE(supportedAxes.end(), supportedAxes.find(MinikinFont::MakeTag('w', 'd', 't', 'h'))); + EXPECT_NE(supportedAxes.end(), supportedAxes.find(MinikinFont::MakeTag('w', 'g', 'h', 't'))); +} + +TEST_F(FontFamilyTest, supportedAxesRestoreFromSerializedBuffer_NoAxisFont) { + const char kFontPath[] = kTestFontDir "/Regular.ttf"; + + std::shared_ptr font(new MinikinFontForTest(kFontPath)); + std::shared_ptr family = + std::make_shared(std::vector({Font(font, FontStyle())})); + + size_t necessaryBytes = family->writeAcceleratorTable(nullptr); + ASSERT_NE(0U, necessaryBytes); + std::vector buffer; + buffer.resize(necessaryBytes); + family->writeAcceleratorTable(buffer.data()); + + std::shared_ptr restoredMultiAxisFamily = + std::make_shared(std::vector({Font(font, FontStyle())}), + buffer.data(), buffer.size()); + + // This font supports no axes. + const std::unordered_set& supportedAxes = family->supportedAxes(); + EXPECT_TRUE(supportedAxes.empty()); +} + } // namespace minikin