summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2023-08-14 15:40:36 -0700
committerXin Li <delphij@google.com>2023-08-14 15:40:36 -0700
commit313ff6f63c603da358a3a5b0ba3448354408a1eb (patch)
treec2bbaabcdf91cd5359247e70a9fd8af5c355507d
parent84f28fb3bc2d352d32ebba584c9d81bd8b9fba77 (diff)
parent6e324dec2a79f267cc0a8d0bf999b7c541efe533 (diff)
downloadminikin-313ff6f63c603da358a3a5b0ba3448354408a1eb.tar.gz
Merge Android U (ab/10368041)tmp_amf_298295554
Bug: 291102124 Merged-In: I78ca8f4144fccc0b21f153d44b2dde13f7c8433e Change-Id: I71dc18a25eaf6eb38c0dc2ed7d5c3408d20ea8a7
-rw-r--r--include/minikin/Buffer.h138
-rw-r--r--include/minikin/CmapCoverage.h2
-rw-r--r--include/minikin/Font.h78
-rw-r--r--include/minikin/FontCollection.h91
-rw-r--r--include/minikin/FontFamily.h87
-rw-r--r--include/minikin/Measurement.h2
-rw-r--r--include/minikin/MinikinFontFactory.h57
-rw-r--r--include/minikin/SparseBitSet.h66
-rw-r--r--include/minikin/SystemFonts.h11
-rw-r--r--libs/minikin/Android.bp1
-rw-r--r--libs/minikin/CmapCoverage.cpp7
-rw-r--r--libs/minikin/Font.cpp90
-rw-r--r--libs/minikin/FontCollection.cpp179
-rw-r--r--libs/minikin/FontFamily.cpp267
-rw-r--r--libs/minikin/Measurement.cpp74
-rw-r--r--libs/minikin/MinikinFontFactory.cpp46
-rw-r--r--libs/minikin/MinikinInternal.h13
-rw-r--r--libs/minikin/SparseBitSet.cpp89
-rw-r--r--libs/minikin/SystemFonts.cpp3
-rw-r--r--tests/perftests/FontCollection.cpp8
-rw-r--r--tests/stresstest/FontFamilyTest.cpp2
-rw-r--r--tests/unittest/BufferTest.cpp38
-rw-r--r--tests/unittest/CmapCoverageTest.cpp192
-rw-r--r--tests/unittest/FontCollectionItemizeTest.cpp30
-rw-r--r--tests/unittest/FontCollectionTest.cpp41
-rw-r--r--tests/unittest/FontFamilyTest.cpp85
-rw-r--r--tests/unittest/FontTest.cpp116
-rw-r--r--tests/unittest/GreedyLineBreakerTest.cpp4
-rw-r--r--tests/unittest/LayoutCoreTest.cpp56
-rw-r--r--tests/unittest/MeasurementTests.cpp90
-rw-r--r--tests/unittest/OptimalLineBreakerTest.cpp4
-rw-r--r--tests/unittest/SparseBitSetTest.cpp4
-rw-r--r--tests/unittest/SystemFontsTest.cpp4
-rw-r--r--tests/util/BufferUtils.h8
-rw-r--r--tests/util/FontTestUtils.cpp15
-rw-r--r--tests/util/FontTestUtils.h6
-rw-r--r--tests/util/FreeTypeMinikinFontForTest.cpp17
-rw-r--r--tests/util/FreeTypeMinikinFontForTest.h19
38 files changed, 1391 insertions, 649 deletions
diff --git a/include/minikin/Buffer.h b/include/minikin/Buffer.h
index 87ba3fd..a4d0104 100644
--- a/include/minikin/Buffer.h
+++ b/include/minikin/Buffer.h
@@ -27,55 +27,88 @@ namespace minikin {
// This is a helper class to read data from a memory buffer.
// This class does not copy memory, and may return pointers to parts of the memory buffer.
// Thus the memory buffer should outlive objects created using this class.
+//
+// Note on alignment:
+// Some CPU archs (e.g. arm32) do not allow misaligned memory access.
+// Therefore, BufferReader and BufferWriter automatically insert paddings
+// to align data records.
+// For the padding to be deterministic, the following conditions must be met:
+// (1) Alignment and size of each data record must be fixed regardless of
+// CPU arch.
+// (2) Alignment for each data record must be a power of 2 (2^n) and
+// must be less than or equal to kMaxAlignment.
+// (3) The head address of the buffer must be aligned at kMaxAlignment.
+//
+// The condition (2) and (3) ensures that 'headAddress % align == 0'
+// and the padding is determined only by the current position.
+// I.e. mCurrent % align == (mCurrent - headAddress) % align.
class BufferReader {
public:
- BufferReader(const void* buffer) : BufferReader(buffer, 0) {}
+ static constexpr size_t kMaxAlignment = 8;
+
+ explicit BufferReader(const void* buffer) : BufferReader(buffer, 0) {}
BufferReader(const void* buffer, uint32_t pos)
- : mData(reinterpret_cast<const uint8_t*>(buffer)), mPos(pos) {}
-
- template <typename T>
- static uint32_t align(uint32_t pos) {
- // This should be true for all types, unless custom alignment attributes are set.
- static_assert(sizeof(T) % alignof(T) == 0, "sizeof(T) must be a multiple of alignof(T)");
- // We align to sizeof(T) instead of alignof(T), because the buffer may be shared between
- // 32-bit processes and 64-bit processes. alignof(T) may change between the two.
- // We assume that T is a type whose size is fixed (e.g. uint32_t).
- return (pos + sizeof(T) - 1) / sizeof(T) * sizeof(T);
+ : mCurrent(reinterpret_cast<const uint8_t*>(buffer) + pos) {}
+
+ // align() adds padding if necessary so that the returned pointer is aligned
+ // at 'align' template parameter (i.e. align<T, _align>(p) % _align == 0).
+ //
+ // By default we align to sizeof(T) instead of alignof(T), because the
+ // buffer may be shared between 32-bit processes and 64-bit processes.
+ // The value of alignof(T) may change between the two.
+ //
+ // If T is a large struct or class, you would need to specify 'align'
+ // template parameter manually.
+ template <typename T, size_t align = sizeof(T)>
+ static const uint8_t* align(const uint8_t* p) {
+ static_assert(align <= kMaxAlignment);
+ static_assert(__builtin_popcount(align) == 1, "align must be a power of 2");
+ constexpr size_t mask = align - 1;
+ intptr_t i = reinterpret_cast<intptr_t>(p);
+ intptr_t aligned = (i + mask) & ~mask;
+ return reinterpret_cast<const uint8_t*>(aligned);
}
- template <typename T>
+ template <typename T, size_t align = sizeof(T)>
const T& read() {
- static_assert(std::is_pod<T>::value, "T must be a POD");
- mPos = BufferReader::align<T>(mPos);
- const T* data = reinterpret_cast<const T*>(mData + mPos);
- mPos += sizeof(T);
+ const T* data = map<T, align>(sizeof(T));
return *data;
}
- template <typename T>
+ template <typename T, size_t align = sizeof(T)>
+ const T* map(uint32_t size) {
+ static_assert(std::is_pod<T>::value, "T must be a POD");
+ mCurrent = BufferReader::align<T, align>(mCurrent);
+ const T* data = reinterpret_cast<const T*>(mCurrent);
+ mCurrent += size;
+ return data;
+ }
+
+ template <typename T, size_t align = sizeof(T)>
void skip() {
static_assert(std::is_pod<T>::value, "T must be a POD");
- mPos = BufferReader::align<T>(mPos);
- mPos += sizeof(T);
+ mCurrent = BufferReader::align<T, align>(mCurrent);
+ mCurrent += sizeof(T);
}
// Return a pointer to an array and its number of elements.
- template <typename T>
+ template <typename T, size_t align = sizeof(T)>
std::pair<const T*, uint32_t> readArray() {
static_assert(std::is_pod<T>::value, "T must be a POD");
+ static_assert(sizeof(T) % align == 0);
uint32_t size = read<uint32_t>();
- mPos = BufferReader::align<T>(mPos);
- const T* data = reinterpret_cast<const T*>(mData + mPos);
- mPos += size * sizeof(T);
+ mCurrent = BufferReader::align<T, align>(mCurrent);
+ const T* data = reinterpret_cast<const T*>(mCurrent);
+ mCurrent += size * sizeof(T);
return std::make_pair(data, size);
}
- template <typename T>
+ template <typename T, size_t align = sizeof(T)>
void skipArray() {
static_assert(std::is_pod<T>::value, "T must be a POD");
uint32_t size = read<uint32_t>();
- mPos = BufferReader::align<T>(mPos);
- mPos += size * sizeof(T);
+ mCurrent = BufferReader::align<T, align>(mCurrent);
+ mCurrent += size * sizeof(T);
}
std::string_view readString() {
@@ -85,20 +118,32 @@ public:
void skipString() { skipArray<char>(); }
- const void* data() const { return mData; }
- size_t pos() const { return mPos; }
+ const void* current() const { return mCurrent; }
private:
- const uint8_t* mData;
- size_t mPos;
+ const uint8_t* mCurrent;
};
// This is a helper class to write data to a memory buffer.
+//
+// BufferWriter does NOT allocate the memory.
+// The typical usage is to use BufferWriter twice; in the first pass, write
+// data with a fake BufferWriter (BufferWriter(nullptr)) to calculate the buffer
+// size. In the second pass, allocate a memory buffer and use a real
+// BufferWriter to write the data.
+// Pseudo code:
+// BufferWriter fakeWriter(nullptr);
+// myData.writeTo(&fakeWriter);
+// void* buffer = malloc(fakeWriter.size());
+// BufferWriter realWriter(buffer);
+// myData.writeTo(&realWriter);
class BufferWriter {
public:
// Create a buffer writer. Passing nullptr creates a fake writer,
// which can be used to measure the buffer size needed.
- BufferWriter(void* buffer) : mData(reinterpret_cast<uint8_t*>(buffer)), mPos(0) {}
+ explicit BufferWriter(void* buffer) : BufferWriter(buffer, 0) {}
+ BufferWriter(void* buffer, uint32_t pos)
+ : mData(reinterpret_cast<uint8_t*>(buffer)), mPos(pos) {}
BufferWriter(BufferWriter&&) = default;
BufferWriter& operator=(BufferWriter&&) = default;
@@ -107,25 +152,35 @@ public:
// Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but
// disables template argument deduction.
// TODO: use std::type_identity_t when C++20 is available.
- template <typename T>
+ template <typename T, size_t align = sizeof(T)>
void write(const std::common_type_t<T>& data) {
- static_assert(std::is_pod<T>::value, "T must be a POD");
- mPos = BufferReader::align<T>(mPos);
- if (mData != nullptr) {
- memcpy(mData + mPos, &data, sizeof(T));
+ T* buf = reserve<T, align>(sizeof(T));
+ if (buf != nullptr) {
+ memcpy(buf, &data, sizeof(T));
}
- mPos += sizeof(T);
+ }
+
+ // Reserve a region and return a pointer to the reserved region.
+ // The reserved region is not initialized.
+ template <typename T, size_t align = sizeof(T)>
+ T* reserve(uint32_t size) {
+ static_assert(std::is_pod<T>::value, "T must be a POD");
+ mPos = BufferWriter::align<T, align>(mPos);
+ uint32_t pos = mPos;
+ mPos += size;
+ return mData == nullptr ? nullptr : reinterpret_cast<T*>(mData + pos);
}
// Write an array of type T.
// Please always specify T explicitly using <>. std::common_type_t<T> resolves to T, but
// disables template argument deduction.
// TODO: use std::type_identity_t when C++20 is available.
- template <typename T>
+ template <typename T, size_t align = sizeof(T)>
void writeArray(const std::common_type_t<T>* data, uint32_t size) {
static_assert(std::is_pod<T>::value, "T must be a POD");
+ static_assert(sizeof(T) % align == 0);
write<uint32_t>(size);
- mPos = BufferReader::align<T>(mPos);
+ mPos = BufferWriter::align<T, align>(mPos);
if (mData != nullptr) {
memcpy(mData + mPos, data, size * sizeof(T));
}
@@ -141,6 +196,11 @@ private:
uint8_t* mData;
size_t mPos;
+ template <typename T, size_t align>
+ size_t align(size_t pos) const {
+ return BufferReader::align<T, align>(mData + pos) - mData;
+ }
+
// Forbid copy and assign.
BufferWriter(const BufferWriter&) = delete;
void operator=(const BufferWriter&) = delete;
diff --git a/include/minikin/CmapCoverage.h b/include/minikin/CmapCoverage.h
index 583593d..898e63b 100644
--- a/include/minikin/CmapCoverage.h
+++ b/include/minikin/CmapCoverage.h
@@ -27,7 +27,7 @@ namespace minikin {
class CmapCoverage {
public:
static SparseBitSet getCoverage(const uint8_t* cmap_data, size_t cmap_size,
- std::vector<std::unique_ptr<SparseBitSet>>* out);
+ std::vector<SparseBitSet>* out);
};
} // namespace minikin
diff --git a/include/minikin/Font.h b/include/minikin/Font.h
index 67feecf..f144bea 100644
--- a/include/minikin/Font.h
+++ b/include/minikin/Font.h
@@ -17,8 +17,10 @@
#ifndef MINIKIN_FONT_H
#define MINIKIN_FONT_H
+#include <gtest/gtest_prod.h>
+
+#include <atomic>
#include <memory>
-#include <mutex>
#include <unordered_set>
#include "minikin/Buffer.h"
@@ -110,29 +112,12 @@ public:
bool mIsSlantSet = false;
};
- // Type for functions to load MinikinFont lazily.
- using TypefaceLoader = std::shared_ptr<MinikinFont>(BufferReader reader);
- // Type for functions to read MinikinFont metadata and return
- // TypefaceLoader.
- using TypefaceReader = TypefaceLoader*(BufferReader* reader);
- // Type for functions to write MinikinFont metadata.
- using TypefaceWriter = void(BufferWriter* writer, const MinikinFont* typeface);
-
- template <TypefaceReader typefaceReader>
- static std::shared_ptr<Font> readFrom(BufferReader* reader, uint32_t localeListId) {
- FontStyle style = FontStyle(reader);
- BufferReader typefaceMetadataReader = *reader;
- TypefaceLoader* typefaceLoader = typefaceReader(reader);
- return std::shared_ptr<Font>(
- new Font(style, typefaceMetadataReader, typefaceLoader, localeListId));
- }
-
- template <TypefaceWriter typefaceWriter>
- void writeTo(BufferWriter* writer) const {
- mStyle.writeTo(writer);
- typefaceWriter(writer, typeface().get());
- }
+ explicit Font(BufferReader* reader);
+ void writeTo(BufferWriter* writer) const;
+ Font(Font&& o) noexcept;
+ Font& operator=(Font&& o) noexcept;
+ ~Font();
// This locale list is just for API compatibility. This is not used in font selection or family
// fallback.
uint32_t getLocaleListId() const { return mLocaleListId; }
@@ -144,46 +129,47 @@ public:
std::unordered_set<AxisTag> getSupportedAxes() const;
private:
+ // ExternalRefs holds references to objects provided by external libraries.
+ // Because creating these external objects is costly,
+ // ExternalRefs is lazily created if Font was created by readFrom().
+ class ExternalRefs {
+ public:
+ ExternalRefs(std::shared_ptr<MinikinFont>&& typeface, HbFontUniquePtr&& baseFont)
+ : mTypeface(std::move(typeface)), mBaseFont(std::move(baseFont)) {}
+
+ std::shared_ptr<MinikinFont> mTypeface;
+ HbFontUniquePtr mBaseFont;
+ };
+
// Use Builder instead.
Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style, HbFontUniquePtr&& baseFont,
uint32_t localeListId)
- : mTypeface(std::move(typeface)),
+ : mExternalRefsHolder(new ExternalRefs(std::move(typeface), std::move(baseFont))),
mStyle(style),
- mBaseFont(std::move(baseFont)),
- mTypefaceLoader(nullptr),
- mTypefaceMetadataReader(nullptr),
- mLocaleListId(localeListId) {}
- Font(FontStyle style, BufferReader typefaceMetadataReader, TypefaceLoader* typefaceLoader,
- uint32_t localeListId)
- : mStyle(style),
- mTypefaceLoader(typefaceLoader),
- mTypefaceMetadataReader(typefaceMetadataReader),
- mLocaleListId(localeListId) {}
+ mLocaleListId(localeListId),
+ mTypefaceMetadataReader(nullptr) {}
+
+ void resetExternalRefs(ExternalRefs* refs);
- void initTypefaceLocked() const EXCLUSIVE_LOCKS_REQUIRED(mTypefaceMutex);
+ const ExternalRefs* getExternalRefs() const;
static HbFontUniquePtr prepareFont(const std::shared_ptr<MinikinFont>& typeface);
static FontStyle analyzeStyle(const HbFontUniquePtr& font);
// Lazy-initialized if created by readFrom().
- mutable std::shared_ptr<MinikinFont> mTypeface GUARDED_BY(mTypefaceMutex);
+ mutable std::atomic<ExternalRefs*> mExternalRefsHolder;
FontStyle mStyle;
- // Lazy-initialized if created by readFrom().
- mutable HbFontUniquePtr mBaseFont GUARDED_BY(mTypefaceMutex);
+ uint32_t mLocaleListId;
- mutable std::mutex mTypefaceMutex;
- // Non-null if created by readFrom().
- TypefaceLoader* mTypefaceLoader;
// Non-null if created by readFrom().
BufferReader mTypefaceMetadataReader;
- uint32_t mLocaleListId;
-
- // Stop copying and moving
- Font(Font&& o) = delete;
- Font& operator=(Font&& o) = delete;
+ // Stop copying.
Font(const Font& o) = delete;
Font& operator=(const Font& o) = delete;
+
+ FRIEND_TEST(FontTest, MoveConstructorTest);
+ FRIEND_TEST(FontTest, MoveAssignmentTest);
};
} // namespace minikin
diff --git a/include/minikin/FontCollection.h b/include/minikin/FontCollection.h
index 98df571..1679abc 100644
--- a/include/minikin/FontCollection.h
+++ b/include/minikin/FontCollection.h
@@ -17,13 +17,12 @@
#ifndef MINIKIN_FONT_COLLECTION_H
#define MINIKIN_FONT_COLLECTION_H
+#include <gtest/gtest_prod.h>
+
#include <memory>
#include <unordered_map>
-#include <unordered_set>
#include <vector>
-#include <gtest/gtest_prod.h>
-
#include "minikin/Buffer.h"
#include "minikin/Font.h"
#include "minikin/FontFamily.h"
@@ -37,43 +36,13 @@ constexpr uint32_t MAX_FAMILY_COUNT = 254;
class FontCollection {
public:
- explicit FontCollection(const std::vector<std::shared_ptr<FontFamily>>& typefaces);
- explicit FontCollection(std::shared_ptr<FontFamily>&& typeface);
-
- template <Font::TypefaceReader typefaceReader>
- static std::vector<std::shared_ptr<FontCollection>> readVector(BufferReader* reader) {
- uint32_t allFontFamiliesCount = reader->read<uint32_t>();
- std::vector<std::shared_ptr<FontFamily>> allFontFamilies;
- allFontFamilies.reserve(allFontFamiliesCount);
- for (uint32_t i = 0; i < allFontFamiliesCount; i++) {
- allFontFamilies.push_back(FontFamily::readFrom<typefaceReader>(reader));
- }
- uint32_t fontCollectionsCount = reader->read<uint32_t>();
- std::vector<std::shared_ptr<FontCollection>> fontCollections;
- fontCollections.reserve(fontCollectionsCount);
- for (uint32_t i = 0; i < fontCollectionsCount; i++) {
- fontCollections.emplace_back(new FontCollection(reader, allFontFamilies));
- }
- return fontCollections;
- }
+ static std::shared_ptr<FontCollection> create(
+ const std::vector<std::shared_ptr<FontFamily>>& typefaces);
+ static std::shared_ptr<FontCollection> create(std::shared_ptr<FontFamily>&& typeface);
- template <Font::TypefaceWriter typefaceWriter>
+ static std::vector<std::shared_ptr<FontCollection>> readVector(BufferReader* reader);
static void writeVector(BufferWriter* writer,
- const std::vector<std::shared_ptr<FontCollection>>& fontCollections) {
- std::vector<std::shared_ptr<FontFamily>> allFontFamilies;
- // Note: operator== for shared_ptr compares raw pointer values.
- std::unordered_map<std::shared_ptr<FontFamily>, uint32_t> fontFamilyToIndexMap;
- collectAllFontFamilies(fontCollections, &allFontFamilies, &fontFamilyToIndexMap);
-
- writer->write<uint32_t>(allFontFamilies.size());
- for (const auto& fontFamily : allFontFamilies) {
- fontFamily->writeTo<typefaceWriter>(writer);
- }
- writer->write<uint32_t>(fontCollections.size());
- for (const auto& fontCollection : fontCollections) {
- fontCollection->writeTo(writer, fontFamilyToIndexMap);
- }
- }
+ const std::vector<std::shared_ptr<FontCollection>>& fontCollections);
// Helper class for representing font family match result in packed bits.
struct FamilyMatchResult {
@@ -190,18 +159,32 @@ public:
// nullptr if none of variations apply to this collection.
std::shared_ptr<FontCollection> createCollectionWithVariation(
const std::vector<FontVariation>& variations);
+ // Creates new FontCollection that uses the specified families as top families and
+ // families from this FontCollection as fallback.
+ std::shared_ptr<FontCollection> createCollectionWithFamilies(
+ std::vector<std::shared_ptr<FontFamily>>&& families) const;
- const std::unordered_set<AxisTag>& getSupportedTags() const { return mSupportedAxes; }
+ size_t getSupportedAxesCount() const { return mSupportedAxesCount; }
+ AxisTag getSupportedAxisAt(size_t index) const { return mSupportedAxes[index]; }
uint32_t getId() const;
- const std::vector<std::shared_ptr<FontFamily>>& getFamilies() const { return mFamilies; }
+ size_t getFamilyCount() const { return mFamilyCount; }
+
+ const std::shared_ptr<FontFamily>& getFamilyAt(size_t index) const {
+ if (mFamilyIndices != nullptr) {
+ index = mFamilyIndices[index];
+ }
+ return (*mMaybeSharedFamilies)[index];
+ }
private:
FRIEND_TEST(FontCollectionTest, bufferTest);
- FontCollection(BufferReader* reader,
- const std::vector<std::shared_ptr<FontFamily>>& allFontFamilies);
+ explicit FontCollection(const std::vector<std::shared_ptr<FontFamily>>& typefaces);
+ FontCollection(
+ BufferReader* reader,
+ const std::shared_ptr<std::vector<std::shared_ptr<FontFamily>>>& allFontFamilies);
// Write fields of the instance, using fontFamilyToIndexMap for finding
// indices for FontFamily.
void writeTo(BufferWriter* writer,
@@ -215,8 +198,9 @@ private:
static const int kLogCharsPerPage = 8;
static const int kPageMask = (1 << kLogCharsPerPage) - 1;
- // mFamilyVec holds the indices of the mFamilies and mRanges holds the range of indices of
- // mFamilyVec. The maximum number of pages is 0x10FF (U+10FFFF >> 8). The maximum number of
+ // mFamilyVec holds the indices of the family (as in getFamilyAt()) and
+ // mRanges holds the range of indices of mFamilyVec.
+ // The maximum number of pages is 0x10FF (U+10FFFF >> 8). The maximum number of
// the fonts is 0xFF. Thus, technically the maximum length of mFamilyVec is 0x10EE01
// (0x10FF * 0xFF). However, in practice, 16-bit integers are enough since most fonts supports
// only limited range of code points.
@@ -237,6 +221,8 @@ private:
uint32_t calcCoverageScore(uint32_t ch, uint32_t vs, uint32_t localeListId,
const std::shared_ptr<FontFamily>& fontFamily) const;
+ bool isPrimaryFamily(const std::shared_ptr<FontFamily>& fontFamily) const;
+
static uint32_t calcLocaleMatchingScore(uint32_t userLocaleListId,
const FontFamily& fontFamily);
@@ -250,7 +236,18 @@ private:
// This vector has pointers to the all font family instances in this collection.
// This vector can't be empty.
- std::vector<std::shared_ptr<FontFamily>> mFamilies;
+ // This vector may be shared with other font collections.
+ // (1) When shared, this vector is a union of all font family instances
+ // shared by multiple font collections.
+ // mFamilyIndices will be non-null in this case.
+ // The i-th family in this collection will be
+ // mMaybeSharedFamilies[mFamilyIndices[i]].
+ // (2) When not shared, mFamilyIndices will be null and
+ // the i-th family in this collection will be mMaybeSharedFamilies[i].
+ // Use getFamilyAt(i) to access the i-th font in this family.
+ std::shared_ptr<std::vector<std::shared_ptr<FontFamily>>> mMaybeSharedFamilies;
+ uint32_t mFamilyCount;
+ const uint32_t* mFamilyIndices;
// Following two vectors are pre-calculated tables for resolving coverage faster.
// For example, to iterate over all fonts which support Unicode code point U+XXYYZZ,
@@ -267,7 +264,9 @@ private:
std::vector<std::shared_ptr<FontFamily>> mVSFamilyVec;
// Set of supported axes in this collection.
- std::unordered_set<AxisTag> mSupportedAxes;
+ uint32_t mSupportedAxesCount;
+ // mSupportedAxes is sorted.
+ std::unique_ptr<AxisTag[]> mSupportedAxes;
// Owns allocated memory if this class is created from font families, otherwise these are
// nullptr.
diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h
index 6169193..6161f5c 100644
--- a/include/minikin/FontFamily.h
+++ b/include/minikin/FontFamily.h
@@ -19,7 +19,6 @@
#include <memory>
#include <string>
-#include <unordered_set>
#include <vector>
#include "minikin/FamilyVariant.h"
@@ -33,32 +32,19 @@ namespace minikin {
class FontFamily {
public:
- explicit FontFamily(std::vector<std::shared_ptr<Font>>&& fonts);
- FontFamily(FamilyVariant variant, std::vector<std::shared_ptr<Font>>&& fonts);
- FontFamily(uint32_t localeListId, FamilyVariant variant,
- std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback);
-
- template <Font::TypefaceReader typefaceReader>
- static std::shared_ptr<FontFamily> readFrom(BufferReader* reader) {
- uint32_t localeListId = readLocaleListInternal(reader);
- uint32_t fontsCount = reader->read<uint32_t>();
- std::vector<std::shared_ptr<Font>> fonts;
- fonts.reserve(fontsCount);
- for (uint32_t i = 0; i < fontsCount; i++) {
- fonts.emplace_back(Font::readFrom<typefaceReader>(reader, localeListId));
- }
- return readFromInternal(reader, std::move(fonts), localeListId);
- }
-
- template <Font::TypefaceWriter typefaceWriter>
- void writeTo(BufferWriter* writer) const {
- writeLocaleListInternal(writer);
- writer->write<uint32_t>(mFonts.size());
- for (const std::shared_ptr<Font>& font : mFonts) {
- font->writeTo<typefaceWriter>(writer);
- }
- writeToInternal(writer);
- }
+ static std::shared_ptr<FontFamily> create(std::vector<std::shared_ptr<Font>>&& fonts);
+ static std::shared_ptr<FontFamily> create(FamilyVariant variant,
+ std::vector<std::shared_ptr<Font>>&& fonts);
+ static std::shared_ptr<FontFamily> create(uint32_t localeListId, FamilyVariant variant,
+ std::vector<std::shared_ptr<Font>>&& fonts,
+ bool isCustomFallback, bool isDefaultFallback);
+
+ FontFamily(FontFamily&&) = default;
+ FontFamily& operator=(FontFamily&&) = default;
+
+ static std::vector<std::shared_ptr<FontFamily>> readVector(BufferReader* reader);
+ static void writeVector(BufferWriter* writer,
+ const std::vector<std::shared_ptr<FontFamily>>& families);
FakedFont getClosestMatch(FontStyle style) const;
@@ -66,13 +52,15 @@ public:
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 mFonts.size(); }
+ size_t getNumFonts() const { return mFontsCount; }
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; }
- const std::unordered_set<AxisTag>& supportedAxes() const { return mSupportedAxes; }
+ size_t getSupportedAxesCount() const { return mSupportedAxesCount; }
+ AxisTag getSupportedAxisAt(size_t index) const { return mSupportedAxes[index]; }
bool isCustomFallback() const { return mIsCustomFallback; }
+ bool isDefaultFallback() const { return mIsDefaultFallback; }
// Get Unicode coverage.
const SparseBitSet& getCoverage() const { return mCoverage; }
@@ -82,7 +70,7 @@ public:
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 !mCmapFmt14Coverage.empty(); }
+ bool hasVSTable() const { return mCmapFmt14CoverageCount != 0; }
// Creates new FontFamily based on this family while applying font variations. Returns nullptr
// if none of variations apply to this family.
@@ -91,29 +79,30 @@ public:
private:
FontFamily(uint32_t localeListId, FamilyVariant variant,
- 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);
-
- static uint32_t readLocaleListInternal(BufferReader* reader);
- static std::shared_ptr<FontFamily> readFromInternal(BufferReader* reader,
- std::vector<std::shared_ptr<Font>>&& fonts,
- uint32_t localeListId);
- void writeLocaleListInternal(BufferWriter* writer) const;
- void writeToInternal(BufferWriter* writer) const;
+ std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback,
+ bool isDefaultFallback);
+ explicit FontFamily(BufferReader* reader, const std::shared_ptr<std::vector<Font>>& fonts);
- void computeCoverage();
+ void writeTo(BufferWriter* writer, uint32_t* fontIndex) const;
- uint32_t mLocaleListId;
- FamilyVariant mVariant;
- std::vector<std::shared_ptr<Font>> mFonts;
- std::unordered_set<AxisTag> mSupportedAxes;
- bool mIsColorEmoji;
- bool mIsCustomFallback;
+ void computeCoverage();
+ // 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;
SparseBitSet mCoverage;
- std::vector<std::unique_ptr<SparseBitSet>> mCmapFmt14Coverage;
+ 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
+ bool mIsDefaultFallback; // 1 byte
MINIKIN_PREVENT_COPY_AND_ASSIGN(FontFamily);
};
diff --git a/include/minikin/Measurement.h b/include/minikin/Measurement.h
index 76f3701..ef0b6a5 100644
--- a/include/minikin/Measurement.h
+++ b/include/minikin/Measurement.h
@@ -28,6 +28,8 @@ namespace minikin {
float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
size_t offset);
+void distributeAdvances(float* advances, const uint16_t* buf, size_t start, size_t count);
+
size_t getOffsetForAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
float advance);
diff --git a/include/minikin/MinikinFontFactory.h b/include/minikin/MinikinFontFactory.h
new file mode 100644
index 0000000..b8c3368
--- /dev/null
+++ b/include/minikin/MinikinFontFactory.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MINIKIN_MINIKIN_FONT_FACTORY_H
+#define MINIKIN_MINIKIN_FONT_FACTORY_H
+
+#include "minikin/Buffer.h"
+#include "minikin/MinikinFont.h"
+
+namespace minikin {
+
+// A class to serialize / deserialize MinikinFont instance into / from memory buffer.
+class MinikinFontFactory {
+public:
+ MinikinFontFactory() {}
+
+ virtual ~MinikinFontFactory() = 0;
+
+ // Create MinikinFont instance from the buffer.
+ virtual std::shared_ptr<MinikinFont> create(BufferReader reader) const = 0;
+
+ // Skip a MinikinFont region in the buffer and advance the reader to the
+ // next position.
+ virtual void skip(BufferReader* reader) const = 0;
+
+ // Serialize MinikinFont into the buffer.
+ virtual void write(BufferWriter* writer, const MinikinFont* minikinFont) const = 0;
+
+ // Return the singleton MinikinFontFactory instance.
+ // setInstance() must be called before any MinikinFont instance is
+ // serialized or deserialized.
+ static const MinikinFontFactory& getInstance();
+
+ // Set the factory instance.
+ // The factory must be a singleton and cannot be changed during the process lifetime.
+ // It is safe to call this method multiple times with the same instance.
+ // This method itself is not thread safe. The call to this method, as well
+ // as deserialized MinikinFont objects, must be synchronized by the caller.
+ static void setInstance(const MinikinFontFactory* factory);
+};
+
+} // namespace minikin
+
+#endif // MINIKIN_MINIKIN_FONT_FACTORY_H
diff --git a/include/minikin/SparseBitSet.h b/include/minikin/SparseBitSet.h
index 3034243..33047e8 100644
--- a/include/minikin/SparseBitSet.h
+++ b/include/minikin/SparseBitSet.h
@@ -19,6 +19,7 @@
#include <minikin/Buffer.h>
#include <sys/types.h>
+
#include <cstdint>
#include <memory>
@@ -34,7 +35,7 @@ namespace minikin {
class SparseBitSet {
public:
// Create an empty bit set.
- SparseBitSet() : mMaxVal(0) {}
+ SparseBitSet() : mData(nullptr) {}
// Initialize the set to a new value, represented by ranges. For
// simplicity, these ranges are arranged as pairs of values,
@@ -52,14 +53,16 @@ public:
// Determine whether the value is included in the set
bool get(uint32_t ch) const {
- if (ch >= mMaxVal) return false;
- const uint32_t* bitmap = &mBitmaps[mIndices[ch >> kLogValuesPerPage]];
+ if (ch >= length()) return false;
+ const uint32_t* bitmap = mData->bitmaps() + mData->indices()[ch >> kLogValuesPerPage];
uint32_t index = ch & kPageMask;
return (bitmap[index >> kLogBitsPerEl] & (kElFirst >> (index & kElMask))) != 0;
}
// One more than the maximum value in the set, or zero if empty
- uint32_t length() const { return mMaxVal; }
+ uint32_t length() const { return mData != nullptr ? mData->mMaxVal : 0; }
+
+ bool empty() const { return mData == nullptr || mData->mMaxVal == 0; }
// The next set bit starting at fromIndex, inclusive, or kNotFound
// if none exists.
@@ -86,20 +89,53 @@ private:
static uint32_t calcNumPages(const uint32_t* ranges, size_t nRanges);
static int CountLeadingZeros(element x);
- uint32_t mMaxVal;
- uint32_t mIndicesCount;
- const uint16_t* mIndices;
- uint32_t mBitmapsCount;
- const element* mBitmaps;
- uint16_t mZeroPageIndex;
-
- // Owns allocated memory if this class is created from ranges, otherwise these are nullptr.
- std::unique_ptr<uint16_t[]> mOwnedIndices;
- std::unique_ptr<element[]> mOwnedBitmaps;
+ // MappableData represents memory block holding SparseBitSet's fields.
+ // 'packed' is used so that the object layout won't change between
+ // 32-bit and 64-bit processes.
+ // 'aligned(4)' is only for optimization.
+ struct __attribute__((packed, aligned(4))) MappableData {
+ uint32_t mMaxVal;
+ uint32_t mIndicesCount;
+ uint32_t mBitmapsCount;
+ uint16_t mZeroPageIndex;
+ // Whether the memory is mapped (BufferReader::map()) or allocated
+ // (malloc()).
+ uint16_t mIsMapped;
+ // mArray packs two arrays:
+ // element mBitmaps[mBitmapsCount];
+ // uint16_t mIndices[mIndicesCount];
+ __attribute__((aligned(4))) uint32_t mArray[];
+ const element* bitmaps() const { return mArray; }
+ element* bitmaps() { return mArray; }
+ const uint16_t* indices() const {
+ return reinterpret_cast<const uint16_t*>(mArray + mBitmapsCount);
+ }
+ uint16_t* indices() { return reinterpret_cast<uint16_t*>(mArray + mBitmapsCount); }
+ size_t size() const { return calcSize(mIndicesCount, mBitmapsCount); }
+ static size_t calcSize(uint32_t indicesCount, uint32_t bitmapsCount) {
+ static_assert(std::is_same<element, uint32_t>::value);
+ static_assert(sizeof(uint32_t) == 4);
+ static_assert(sizeof(uint16_t) == 2);
+ // Round-up indicesCount / 2
+ size_t arrayCount = bitmapsCount + (indicesCount + 1) / 2;
+ return offsetof(MappableData, mArray) + sizeof(uint32_t) * arrayCount;
+ }
+ static MappableData* allocate(uint32_t indicesCount, uint32_t bitmapsCount);
+ };
+
+ // MappableDataDeleter does NOT call free() if the data is on a memory map.
+ class MappableDataDeleter {
+ public:
+ void operator()(const MappableData* data) const {
+ if (data != nullptr && !data->mIsMapped) free((void*)data);
+ }
+ };
+
+ std::unique_ptr<const MappableData, MappableDataDeleter> mData;
// Forbid copy and assign.
SparseBitSet(const SparseBitSet&) = delete;
- void operator=(const SparseBitSet&) = delete;
+ SparseBitSet& operator=(const SparseBitSet&) = delete;
};
} // namespace minikin
diff --git a/include/minikin/SystemFonts.h b/include/minikin/SystemFonts.h
index cf4ab75..4ef2705 100644
--- a/include/minikin/SystemFonts.h
+++ b/include/minikin/SystemFonts.h
@@ -50,11 +50,6 @@ public:
}
// This obtains a mutex inside, so do not call this method inside callback.
- static void getFontMap(
- std::function<void(const std::vector<std::shared_ptr<FontCollection>>&)> func) {
- return getInstance().getFontMapInternal(func);
- }
-
static void getFontSet(std::function<void(const std::vector<std::shared_ptr<Font>>&)> func) {
return getInstance().getFontSetInternal(func);
}
@@ -81,12 +76,6 @@ protected:
mCollections.emplace_back(std::move(collections));
}
- void getFontMapInternal(
- std::function<void(const std::vector<std::shared_ptr<FontCollection>>&)> func) {
- std::lock_guard<std::mutex> lock(mMutex);
- func(mCollections);
- }
-
void getFontSetInternal(std::function<void(const std::vector<std::shared_ptr<Font>>&)> func) {
std::lock_guard<std::mutex> lock(mMutex);
if (!mFonts) {
diff --git a/libs/minikin/Android.bp b/libs/minikin/Android.bp
index a6128c8..5c7abf2 100644
--- a/libs/minikin/Android.bp
+++ b/libs/minikin/Android.bp
@@ -50,6 +50,7 @@ cc_library {
"LocaleListCache.cpp",
"MeasuredText.cpp",
"Measurement.cpp",
+ "MinikinFontFactory.cpp",
"MinikinInternal.cpp",
"OptimalLineBreaker.cpp",
"SparseBitSet.cpp",
diff --git a/libs/minikin/CmapCoverage.cpp b/libs/minikin/CmapCoverage.cpp
index 8d04ce8..fd1a7f1 100644
--- a/libs/minikin/CmapCoverage.cpp
+++ b/libs/minikin/CmapCoverage.cpp
@@ -368,8 +368,7 @@ static bool getVSCoverage(std::vector<uint32_t>* out_ranges, const uint8_t* data
return true;
}
-static void getCoverageFormat14(std::vector<std::unique_ptr<SparseBitSet>>* out,
- const uint8_t* data, size_t size,
+static void getCoverageFormat14(std::vector<SparseBitSet>* out, const uint8_t* data, size_t size,
const SparseBitSet& baseCoverage) {
constexpr size_t kHeaderSize = 10;
constexpr size_t kRecordSize = 11;
@@ -417,14 +416,14 @@ static void getCoverageFormat14(std::vector<std::unique_ptr<SparseBitSet>>* out,
if (out->size() < vsIndex + 1) {
out->resize(vsIndex + 1);
}
- (*out)[vsIndex].reset(new SparseBitSet(ranges.data(), ranges.size() >> 1));
+ (*out)[vsIndex] = SparseBitSet(ranges.data(), ranges.size() >> 1);
}
out->shrink_to_fit();
}
SparseBitSet CmapCoverage::getCoverage(const uint8_t* cmap_data, size_t cmap_size,
- std::vector<std::unique_ptr<SparseBitSet>>* out) {
+ std::vector<SparseBitSet>* out) {
constexpr size_t kHeaderSize = 4;
constexpr size_t kNumTablesOffset = 2;
constexpr size_t kTableSize = 8;
diff --git a/libs/minikin/Font.cpp b/libs/minikin/Font.cpp
index c2e74b7..bf7450a 100644
--- a/libs/minikin/Font.cpp
+++ b/libs/minikin/Font.cpp
@@ -18,17 +18,18 @@
#include "minikin/Font.h"
-#include <vector>
-
#include <hb-ot.h>
#include <hb.h>
#include <log/log.h>
-#include "minikin/HbUtils.h"
-#include "minikin/MinikinFont.h"
+#include <vector>
#include "FontUtils.h"
+#include "LocaleListCache.h"
#include "MinikinInternal.h"
+#include "minikin/HbUtils.h"
+#include "minikin/MinikinFont.h"
+#include "minikin/MinikinFontFactory.h"
namespace minikin {
@@ -51,25 +52,80 @@ std::shared_ptr<Font> Font::Builder::build() {
std::move(font), mLocaleListId));
}
+Font::Font(BufferReader* reader) : mExternalRefsHolder(nullptr), mTypefaceMetadataReader(nullptr) {
+ mStyle = FontStyle(reader);
+ mLocaleListId = LocaleListCache::readFrom(reader);
+ mTypefaceMetadataReader = *reader;
+ MinikinFontFactory::getInstance().skip(reader);
+}
+
+void Font::writeTo(BufferWriter* writer) const {
+ mStyle.writeTo(writer);
+ LocaleListCache::writeTo(writer, mLocaleListId);
+ MinikinFontFactory::getInstance().write(writer, typeface().get());
+}
+
+Font::Font(Font&& o) noexcept
+ : mStyle(o.mStyle),
+ mLocaleListId(o.mLocaleListId),
+ mTypefaceMetadataReader(o.mTypefaceMetadataReader) {
+ mExternalRefsHolder.store(o.mExternalRefsHolder.exchange(nullptr));
+}
+
+Font& Font::operator=(Font&& o) noexcept {
+ resetExternalRefs(o.mExternalRefsHolder.exchange(nullptr));
+ mStyle = o.mStyle;
+ mLocaleListId = o.mLocaleListId;
+ mTypefaceMetadataReader = o.mTypefaceMetadataReader;
+ return *this;
+}
+
+Font::~Font() {
+ resetExternalRefs(nullptr);
+}
+
+void Font::resetExternalRefs(ExternalRefs* refs) {
+ ExternalRefs* oldRefs = mExternalRefsHolder.exchange(refs);
+ if (oldRefs != nullptr) {
+ delete oldRefs;
+ }
+}
+
const std::shared_ptr<MinikinFont>& Font::typeface() const {
- std::lock_guard lock(mTypefaceMutex);
- if (mTypeface) return mTypeface;
- initTypefaceLocked();
- return mTypeface;
+ return getExternalRefs()->mTypeface;
}
const HbFontUniquePtr& Font::baseFont() const {
- std::lock_guard lock(mTypefaceMutex);
- if (mBaseFont) return mBaseFont;
- initTypefaceLocked();
- mBaseFont = prepareFont(mTypeface);
- return mBaseFont;
+ return getExternalRefs()->mBaseFont;
}
-void Font::initTypefaceLocked() const {
- if (mTypeface) return;
- MINIKIN_ASSERT(mTypefaceLoader, "mTypefaceLoader should not be empty when mTypeface is null");
- mTypeface = mTypefaceLoader(mTypefaceMetadataReader);
+const Font::ExternalRefs* Font::getExternalRefs() const {
+ // Thread safety note: getExternalRefs() is thread-safe.
+ // getExternalRefs() returns the first ExternalRefs set to mExternalRefsHolder.
+ // When multiple threads called getExternalRefs() at the same time and
+ // mExternalRefsHolder is not set, multiple ExternalRefs may be created,
+ // but only one ExternalRefs will be set to mExternalRefsHolder and
+ // others will be deleted.
+ Font::ExternalRefs* externalRefs = mExternalRefsHolder.load();
+ if (externalRefs) return externalRefs;
+ // mExternalRefsHolder is null. Try creating an ExternalRefs.
+ std::shared_ptr<MinikinFont> typeface =
+ MinikinFontFactory::getInstance().create(mTypefaceMetadataReader);
+ HbFontUniquePtr font = prepareFont(typeface);
+ Font::ExternalRefs* newExternalRefs =
+ new Font::ExternalRefs(std::move(typeface), std::move(font));
+ // Set the new ExternalRefs to mExternalRefsHolder if it is still null.
+ Font::ExternalRefs* expected = nullptr;
+ if (mExternalRefsHolder.compare_exchange_strong(expected, newExternalRefs)) {
+ return newExternalRefs;
+ } else {
+ // Another thread has already created and set an ExternalRefs.
+ // Delete ours and use theirs instead.
+ delete newExternalRefs;
+ // compare_exchange_strong writes the stored value into 'expected'
+ // when comparison fails.
+ return expected;
+ }
}
// static
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 38fc022..0c1a173 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -22,6 +22,7 @@
#include <unicode/unorm2.h>
#include <algorithm>
+#include <unordered_set>
#include "Locale.h"
#include "LocaleListCache.h"
@@ -121,13 +122,22 @@ uint32_t getGlyphScore(U16StringPiece text, uint32_t start, uint32_t end,
} // namespace
-FontCollection::FontCollection(std::shared_ptr<FontFamily>&& typeface) : mMaxChar(0) {
+// static
+std::shared_ptr<FontCollection> FontCollection::create(std::shared_ptr<FontFamily>&& typeface) {
std::vector<std::shared_ptr<FontFamily>> typefaces;
typefaces.push_back(typeface);
- init(typefaces);
+ return create(typefaces);
+}
+
+// static
+std::shared_ptr<FontCollection> FontCollection::create(
+ const vector<std::shared_ptr<FontFamily>>& typefaces) {
+ // TODO(b/174672300): Revert back to make_shared.
+ return std::shared_ptr<FontCollection>(new FontCollection(typefaces));
}
-FontCollection::FontCollection(const vector<std::shared_ptr<FontFamily>>& typefaces) : mMaxChar(0) {
+FontCollection::FontCollection(const vector<std::shared_ptr<FontFamily>>& typefaces)
+ : mMaxChar(0), mSupportedAxes(nullptr) {
init(typefaces);
}
@@ -136,26 +146,38 @@ void FontCollection::init(const vector<std::shared_ptr<FontFamily>>& typefaces)
vector<uint32_t> lastChar;
size_t nTypefaces = typefaces.size();
const FontStyle defaultStyle;
+ auto families = std::make_shared<vector<std::shared_ptr<FontFamily>>>();
+ std::unordered_set<AxisTag> supportedAxesSet;
for (size_t i = 0; i < nTypefaces; i++) {
const std::shared_ptr<FontFamily>& family = typefaces[i];
if (family->getClosestMatch(defaultStyle).font == nullptr) {
continue;
}
const SparseBitSet& coverage = family->getCoverage();
- mFamilies.push_back(family); // emplace_back would be better
+ families->emplace_back(family);
if (family->hasVSTable()) {
mVSFamilyVec.push_back(family);
}
mMaxChar = max(mMaxChar, coverage.length());
lastChar.push_back(coverage.nextSetBit(0));
- const std::unordered_set<AxisTag>& supportedAxes = family->supportedAxes();
- mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
+ for (size_t i = 0; i < family->getSupportedAxesCount(); i++) {
+ supportedAxesSet.insert(family->getSupportedAxisAt(i));
+ }
}
- nTypefaces = mFamilies.size();
- MINIKIN_ASSERT(nTypefaces > 0, "Font collection must have at least one valid typeface");
- MINIKIN_ASSERT(nTypefaces <= MAX_FAMILY_COUNT,
+ // mMaybeSharedFamilies is not shared.
+ mMaybeSharedFamilies = families;
+ mFamilyCount = families->size();
+ mFamilyIndices = nullptr;
+ MINIKIN_ASSERT(mFamilyCount > 0, "Font collection must have at least one valid typeface");
+ MINIKIN_ASSERT(mFamilyCount <= MAX_FAMILY_COUNT,
"Font collection may only have up to %d font families.", MAX_FAMILY_COUNT);
+ // Although OpenType supports up to 2^16-1 axes per font,
+ // mSupportedAxesCount may exceed 2^16-1 as we have multiple fonts.
+ mSupportedAxesCount = static_cast<uint32_t>(supportedAxesSet.size());
+ if (mSupportedAxesCount > 0) {
+ mSupportedAxes = sortedArrayFromSet(supportedAxesSet);
+ }
size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage;
// TODO: Use variation selector map for mRanges construction.
// A font can have a glyph for a base code point and variation selector pair but no glyph for
@@ -167,9 +189,9 @@ void FontCollection::init(const vector<std::shared_ptr<FontFamily>>& typefaces)
for (size_t i = 0; i < nPages; i++) {
Range* range = &mOwnedRanges[i];
range->start = mOwnedFamilyVec.size();
- for (size_t j = 0; j < nTypefaces; j++) {
+ for (size_t j = 0; j < getFamilyCount(); j++) {
if (lastChar[j] < (i + 1) << kLogCharsPerPage) {
- const std::shared_ptr<FontFamily>& family = mFamilies[j];
+ const std::shared_ptr<FontFamily>& family = getFamilyAt(j);
mOwnedFamilyVec.push_back(static_cast<uint8_t>(j));
uint32_t nextChar = family->getCoverage().nextSetBit((i + 1) << kLogCharsPerPage);
lastChar[j] = nextChar;
@@ -184,52 +206,78 @@ void FontCollection::init(const vector<std::shared_ptr<FontFamily>>& typefaces)
mFamilyVecCount = mOwnedFamilyVec.size();
}
-FontCollection::FontCollection(BufferReader* reader,
- const std::vector<std::shared_ptr<FontFamily>>& families) {
+FontCollection::FontCollection(
+ BufferReader* reader,
+ const std::shared_ptr<std::vector<std::shared_ptr<FontFamily>>>& families)
+ : mSupportedAxes(nullptr) {
mId = gNextCollectionId++;
mMaxChar = reader->read<uint32_t>();
- uint32_t familiesCount = reader->read<uint32_t>();
- mFamilies.reserve(familiesCount);
- for (uint32_t i = 0; i < familiesCount; i++) {
- uint32_t index = reader->read<uint32_t>();
- if (index >= families.size()) {
- ALOGE("Invalid FontFamily index: %zu", (size_t)index);
- } else {
- mFamilies.push_back(families[index]);
- if (families[index]->hasVSTable()) {
- mVSFamilyVec.push_back(families[index]);
- }
- }
+ mMaybeSharedFamilies = families;
+ std::tie(mFamilyIndices, mFamilyCount) = reader->readArray<uint32_t>();
+ for (size_t i = 0; i < getFamilyCount(); i++) {
+ const auto& family = getFamilyAt(i);
+ if (family->hasVSTable()) mVSFamilyVec.emplace_back(family);
}
// Range is two packed uint16_t
static_assert(sizeof(Range) == 4);
std::tie(mRanges, mRangesCount) = reader->readArray<Range>();
std::tie(mFamilyVec, mFamilyVecCount) = reader->readArray<uint8_t>();
const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
- mSupportedAxes.insert(axesPtr, axesPtr + axesCount);
+ mSupportedAxesCount = axesCount;
+ if (axesCount > 0) {
+ mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
+ std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
+ }
}
void FontCollection::writeTo(BufferWriter* writer,
const std::unordered_map<std::shared_ptr<FontFamily>, uint32_t>&
fontFamilyToIndexMap) const {
writer->write<uint32_t>(mMaxChar);
- writer->write<uint32_t>(mFamilies.size());
- for (const std::shared_ptr<FontFamily>& fontFamily : mFamilies) {
+ std::vector<uint32_t> indices;
+ indices.reserve(getFamilyCount());
+ for (size_t i = 0; i < getFamilyCount(); ++i) {
+ const std::shared_ptr<FontFamily>& fontFamily = getFamilyAt(i);
auto it = fontFamilyToIndexMap.find(fontFamily);
if (it == fontFamilyToIndexMap.end()) {
ALOGE("fontFamily not found in fontFamilyToIndexMap");
- writer->write<uint32_t>(-1);
} else {
- writer->write<uint32_t>(it->second);
+ indices.push_back(it->second);
}
}
+ writer->writeArray<uint32_t>(indices.data(), indices.size());
writer->writeArray<Range>(mRanges, mRangesCount);
writer->writeArray<uint8_t>(mFamilyVec, mFamilyVecCount);
// No need to serialize mVSFamilyVec as it can be reconstructed easily from mFamilies.
- 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->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
+}
+
+// static
+std::vector<std::shared_ptr<FontCollection>> FontCollection::readVector(BufferReader* reader) {
+ auto allFontFamilies = std::make_shared<std::vector<std::shared_ptr<FontFamily>>>(
+ FontFamily::readVector(reader));
+ uint32_t count = reader->read<uint32_t>();
+ std::vector<std::shared_ptr<FontCollection>> fontCollections;
+ fontCollections.reserve(count);
+ for (uint32_t i = 0; i < count; i++) {
+ fontCollections.emplace_back(new FontCollection(reader, allFontFamilies));
+ }
+ return fontCollections;
+}
+
+// static
+void FontCollection::writeVector(
+ BufferWriter* writer, const std::vector<std::shared_ptr<FontCollection>>& fontCollections) {
+ std::vector<std::shared_ptr<FontFamily>> allFontFamilies;
+ // Note: operator== for shared_ptr compares raw pointer values.
+ std::unordered_map<std::shared_ptr<FontFamily>, uint32_t> fontFamilyToIndexMap;
+ collectAllFontFamilies(fontCollections, &allFontFamilies, &fontFamilyToIndexMap);
+
+ FontFamily::writeVector(writer, allFontFamilies);
+ writer->write<uint32_t>(fontCollections.size());
+ for (const auto& fontCollection : fontCollections) {
+ fontCollection->writeTo(writer, fontFamilyToIndexMap);
+ }
}
// static
@@ -238,7 +286,8 @@ void FontCollection::collectAllFontFamilies(
std::vector<std::shared_ptr<FontFamily>>* outAllFontFamilies,
std::unordered_map<std::shared_ptr<FontFamily>, uint32_t>* outFontFamilyToIndexMap) {
for (const auto& fontCollection : fontCollections) {
- for (const std::shared_ptr<FontFamily>& fontFamily : fontCollection->mFamilies) {
+ for (size_t i = 0; i < fontCollection->getFamilyCount(); ++i) {
+ const std::shared_ptr<FontFamily>& fontFamily = fontCollection->getFamilyAt(i);
bool inserted =
outFontFamilyToIndexMap->emplace(fontFamily, outAllFontFamilies->size()).second;
if (inserted) {
@@ -286,6 +335,22 @@ uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, FamilyVariant
return coverageScore << 29 | localeScore << 1 | variantScore;
}
+// Returns true if
+// - the fontFamily is a developer specified custom fallback.
+// - no custom fallback is provided and the fontFamily is a default fallback.
+bool FontCollection::isPrimaryFamily(const std::shared_ptr<FontFamily>& fontFamily) const {
+ // If the font family is provided by developers, it is primary.
+ if (fontFamily->isCustomFallback()) {
+ return true;
+ }
+
+ if (getFamilyAt(0)->isCustomFallback()) {
+ return false;
+ } else {
+ return fontFamily->isDefaultFallback();
+ }
+}
+
// Calculates a font score based on variation sequence coverage.
// - Returns kUnsupportedFontScore if the font doesn't support the variation sequence or its base
// character.
@@ -304,7 +369,7 @@ uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs, uint32_t lo
return kUnsupportedFontScore;
}
- if ((vs == 0 || hasVSGlyph) && (mFamilies[0] == fontFamily || fontFamily->isCustomFallback())) {
+ if ((vs == 0 || hasVSGlyph) && isPrimaryFamily(fontFamily)) {
// If the first font family supports the given character or variation sequence, always use
// it.
return kFirstFontScore;
@@ -404,7 +469,7 @@ FontCollection::FamilyMatchResult FontCollection::getFamilyForChar(uint32_t ch,
Range range = mRanges[ch >> kLogCharsPerPage];
if (vs != 0) {
- range = {0, static_cast<uint16_t>(mFamilies.size())};
+ range = {0, static_cast<uint16_t>(getFamilyCount())};
}
uint32_t bestScore = kUnsupportedFontScore;
@@ -412,7 +477,7 @@ FontCollection::FamilyMatchResult FontCollection::getFamilyForChar(uint32_t ch,
for (size_t i = range.start; i < range.end; i++) {
const uint8_t familyIndex = vs == 0 ? mFamilyVec[i] : i;
- const std::shared_ptr<FontFamily>& family = mFamilies[familyIndex];
+ const std::shared_ptr<FontFamily>& family = getFamilyAt(familyIndex);
const uint32_t score = calcFamilyScore(ch, vs, variant, localeListId, family);
if (score == kFirstFontScore) {
// If the first font family supports the given character or variation sequence, always
@@ -505,8 +570,9 @@ bool FontCollection::hasVariationSelector(uint32_t baseCodepoint,
// sequences, since Unicode is adding variation sequences more frequently now and may even move
// towards allowing text and emoji variation selectors on any character.
if (variationSelector == TEXT_STYLE_VS) {
- for (size_t i = 0; i < mFamilies.size(); ++i) {
- if (!mFamilies[i]->isColorEmojiFamily() && mFamilies[i]->hasGlyph(baseCodepoint, 0)) {
+ for (size_t i = 0; i < getFamilyCount(); ++i) {
+ const std::shared_ptr<FontFamily>& family = getFamilyAt(i);
+ if (!family->isColorEmojiFamily() && family->hasGlyph(baseCodepoint, 0)) {
return true;
}
}
@@ -586,12 +652,12 @@ std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, Fo
} else if (!lastFamilyIndices.empty() && (isStickyAllowlisted(ch) || isCombining(ch))) {
// Continue using existing font as long as it has coverage and is whitelisted.
- const std::shared_ptr<FontFamily>& lastFamily = mFamilies[lastFamilyIndices[0]];
+ const std::shared_ptr<FontFamily>& lastFamily = getFamilyAt(lastFamilyIndices[0]);
if (lastFamily->isColorEmojiFamily()) {
// If the last family is color emoji font, find the longest family.
shouldContinueRun = false;
for (uint8_t ix : lastFamilyIndices) {
- shouldContinueRun |= mFamilies[ix]->getCoverage().get(ch);
+ shouldContinueRun |= getFamilyAt(ix)->getCoverage().get(ch);
}
} else {
shouldContinueRun = lastFamily->getCoverage().get(ch);
@@ -605,7 +671,7 @@ std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, Fo
if (utf16Pos == 0 || lastFamilyIndices.empty()) {
breakRun = true;
} else {
- const std::shared_ptr<FontFamily>& lastFamily = mFamilies[lastFamilyIndices[0]];
+ const std::shared_ptr<FontFamily>& lastFamily = getFamilyAt(lastFamilyIndices[0]);
if (lastFamily->isColorEmojiFamily()) {
FamilyMatchResult intersection =
FamilyMatchResult::intersect(familyIndices, lastFamilyIndices);
@@ -637,7 +703,7 @@ std::vector<FontCollection::Run> FontCollection::itemize(U16StringPiece text, Fo
if (utf16Pos != 0 &&
(isCombining(ch) || (isEmojiModifier(ch) && isEmojiBase(prevCh)))) {
for (uint8_t ix : familyIndices) {
- if (mFamilies[ix]->getCoverage().get(prevCh)) {
+ if (getFamilyAt(ix)->getCoverage().get(prevCh)) {
const size_t prevChLength = U16_LENGTH(prevCh);
if (run != nullptr) {
run->end -= prevChLength;
@@ -696,10 +762,10 @@ FakedFont FontCollection::getBestFont(U16StringPiece text, const Run& run, FontS
uint8_t bestIndex = 0;
uint32_t bestScore = 0xFFFFFFFF;
- const std::shared_ptr<FontFamily>& family = mFamilies[run.familyMatch[0]];
+ const std::shared_ptr<FontFamily>& family = getFamilyAt(run.familyMatch[0]);
if (family->isColorEmojiFamily() && run.familyMatch.size() > 1) {
for (size_t i = 0; i < run.familyMatch.size(); ++i) {
- const std::shared_ptr<FontFamily>& family = mFamilies[run.familyMatch[i]];
+ const std::shared_ptr<FontFamily>& family = getFamilyAt(run.familyMatch[i]);
const HbFontUniquePtr& font = family->getFont(0)->baseFont();
uint32_t score = getGlyphScore(text, run.start, run.end, font);
@@ -711,22 +777,23 @@ FakedFont FontCollection::getBestFont(U16StringPiece text, const Run& run, FontS
} else {
bestIndex = run.familyMatch[0];
}
- return mFamilies[bestIndex]->getClosestMatch(style);
+ return getFamilyAt(bestIndex)->getClosestMatch(style);
}
FakedFont FontCollection::baseFontFaked(FontStyle style) {
- return mFamilies[0]->getClosestMatch(style);
+ return getFamilyAt(0)->getClosestMatch(style);
}
std::shared_ptr<FontCollection> FontCollection::createCollectionWithVariation(
const std::vector<FontVariation>& variations) {
- if (variations.empty() || mSupportedAxes.empty()) {
+ if (variations.empty() || mSupportedAxesCount == 0) {
return nullptr;
}
bool hasSupportedAxis = false;
for (const FontVariation& variation : variations) {
- if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
+ if (std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
+ variation.axisTag)) {
hasSupportedAxis = true;
break;
}
@@ -737,7 +804,8 @@ std::shared_ptr<FontCollection> FontCollection::createCollectionWithVariation(
}
std::vector<std::shared_ptr<FontFamily>> families;
- for (const std::shared_ptr<FontFamily>& family : mFamilies) {
+ for (size_t i = 0; i < getFamilyCount(); ++i) {
+ const std::shared_ptr<FontFamily>& family = getFamilyAt(i);
std::shared_ptr<FontFamily> newFamily = family->createFamilyWithVariation(variations);
if (newFamily) {
families.push_back(newFamily);
@@ -749,6 +817,15 @@ std::shared_ptr<FontCollection> FontCollection::createCollectionWithVariation(
return std::shared_ptr<FontCollection>(new FontCollection(families));
}
+std::shared_ptr<FontCollection> FontCollection::createCollectionWithFamilies(
+ std::vector<std::shared_ptr<FontFamily>>&& families) const {
+ families.reserve(families.size() + getFamilyCount());
+ for (size_t i = 0; i < getFamilyCount(); i++) {
+ families.push_back(getFamilyAt(i));
+ }
+ return FontCollection::create(families);
+}
+
uint32_t FontCollection::getId() const {
return mId;
}
diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp
index f1fd00a..836674c 100644
--- a/libs/minikin/FontFamily.cpp
+++ b/libs/minikin/FontFamily.cpp
@@ -18,118 +18,190 @@
#include "minikin/FontFamily.h"
-#include <algorithm>
-#include <vector>
-
#include <log/log.h>
-#include "minikin/CmapCoverage.h"
-#include "minikin/FamilyVariant.h"
-#include "minikin/HbUtils.h"
-#include "minikin/MinikinFont.h"
+#include <algorithm>
+#include <unordered_set>
+#include <vector>
#include "FontUtils.h"
#include "Locale.h"
#include "LocaleListCache.h"
#include "MinikinInternal.h"
+#include "minikin/CmapCoverage.h"
+#include "minikin/FamilyVariant.h"
+#include "minikin/HbUtils.h"
+#include "minikin/MinikinFont.h"
namespace minikin {
-FontFamily::FontFamily(std::vector<std::shared_ptr<Font>>&& fonts)
- : FontFamily(FamilyVariant::DEFAULT, std::move(fonts)) {}
+// static
+std::shared_ptr<FontFamily> FontFamily::create(std::vector<std::shared_ptr<Font>>&& fonts) {
+ return create(FamilyVariant::DEFAULT, std::move(fonts));
+}
-FontFamily::FontFamily(FamilyVariant variant, std::vector<std::shared_ptr<Font>>&& fonts)
- : FontFamily(kEmptyLocaleListId, variant, std::move(fonts), false /* isCustomFallback */) {}
+// static
+std::shared_ptr<FontFamily> FontFamily::create(FamilyVariant variant,
+ std::vector<std::shared_ptr<Font>>&& fonts) {
+ return create(kEmptyLocaleListId, variant, std::move(fonts), false /* isCustomFallback */,
+ false /* isDefaultFallback */);
+}
+
+// static
+std::shared_ptr<FontFamily> FontFamily::create(uint32_t localeListId, FamilyVariant variant,
+ std::vector<std::shared_ptr<Font>>&& fonts,
+ bool isCustomFallback, bool isDefaultFallback) {
+ // TODO(b/174672300): Revert back to make_shared.
+ return std::shared_ptr<FontFamily>(new FontFamily(localeListId, variant, std::move(fonts),
+ isCustomFallback, isDefaultFallback));
+}
FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
- std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback)
- : mLocaleListId(localeListId),
+ std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback,
+ bool isDefaultFallback)
+ : mFonts(std::make_unique<std::shared_ptr<Font>[]>(fonts.size())),
+ // computeCoverage may update supported axes and coverages later.
+ mSupportedAxes(nullptr),
+ mCoverage(),
+ mCmapFmt14Coverage(nullptr),
+ mLocaleListId(localeListId),
+ mFontsCount(static_cast<uint32_t>(fonts.size())),
+ mSupportedAxesCount(0),
+ mCmapFmt14CoverageCount(0),
mVariant(variant),
- mFonts(std::move(fonts)),
mIsColorEmoji(LocaleListCache::getById(localeListId).getEmojiStyle() ==
EmojiStyle::EMOJI),
- mIsCustomFallback(isCustomFallback) {
- MINIKIN_ASSERT(!mFonts.empty(), "FontFamily must contain at least one font.");
+ mIsCustomFallback(isCustomFallback),
+ mIsDefaultFallback(isDefaultFallback) {
+ 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]);
+ }
computeCoverage();
}
-FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
- 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),
- mCoverage(std::move(coverage)),
- mCmapFmt14Coverage(std::move(cmapFmt14Coverage)) {}
-
-// Read fields other than mFonts, mLocaleList.
-// static
-std::shared_ptr<FontFamily> FontFamily::readFromInternal(BufferReader* reader,
- std::vector<std::shared_ptr<Font>>&& fonts,
- uint32_t localeListId) {
+FontFamily::FontFamily(BufferReader* reader, const std::shared_ptr<std::vector<Font>>& allFonts)
+ : mSupportedAxes(nullptr), mCmapFmt14Coverage(nullptr) {
+ mLocaleListId = LocaleListCache::readFrom(reader);
+ mFontsCount = reader->read<uint32_t>();
+ mFonts = std::make_unique<std::shared_ptr<Font>[]>(mFontsCount);
+ for (size_t i = 0; i < mFontsCount; i++) {
+ uint32_t fontIndex = reader->read<uint32_t>();
+ // Use aliasing constructor to save memory.
+ // See the comments on FontFamily::readVector for details.
+ mFonts[i] = std::shared_ptr<Font>(allFonts, &(*allFonts)[fontIndex]);
+ }
// FamilyVariant is uint8_t
static_assert(sizeof(FamilyVariant) == 1);
- FamilyVariant variant = reader->read<FamilyVariant>();
+ mVariant = reader->read<FamilyVariant>();
// AxisTag is uint32_t
static_assert(sizeof(AxisTag) == 4);
const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
- 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);
+ mSupportedAxesCount = axesCount;
+ if (axesCount > 0) {
+ mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
+ std::copy(axesPtr, axesPtr + axesCount, mSupportedAxes.get());
+ }
+ mIsColorEmoji = static_cast<bool>(reader->read<uint8_t>());
+ mIsCustomFallback = static_cast<bool>(reader->read<uint8_t>());
+ mIsDefaultFallback = static_cast<bool>(reader->read<uint8_t>());
+ mCoverage = SparseBitSet(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>();
- 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), std::move(supportedAxes), isColorEmoji,
- isCustomFallback, std::move(coverage), std::move(cmapFmt14Coverage)));
-}
-
-// static
-uint32_t FontFamily::readLocaleListInternal(BufferReader* reader) {
- return LocaleListCache::readFrom(reader);
+ mCmapFmt14CoverageCount = reader->read<uint32_t>();
+ if (mCmapFmt14CoverageCount > 0) {
+ mCmapFmt14Coverage = std::make_unique<SparseBitSet[]>(mCmapFmt14CoverageCount);
+ uint32_t cmapFmt14CoverageEntryCount = reader->read<uint32_t>();
+ for (uint32_t i = 0; i < cmapFmt14CoverageEntryCount; i++) {
+ uint32_t index = reader->read<uint32_t>();
+ mCmapFmt14Coverage[index] = SparseBitSet(reader);
+ }
+ }
}
-// Write fields other than mFonts.
-void FontFamily::writeToInternal(BufferWriter* writer) const {
+void FontFamily::writeTo(BufferWriter* writer, uint32_t* fontIndex) const {
+ LocaleListCache::writeTo(writer, mLocaleListId);
+ writer->write<uint32_t>(mFontsCount);
+ for (size_t i = 0; i < mFontsCount; i++) {
+ writer->write<uint32_t>(*fontIndex);
+ (*fontIndex)++;
+ }
writer->write<FamilyVariant>(mVariant);
- 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->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
writer->write<uint8_t>(mIsColorEmoji);
writer->write<uint8_t>(mIsCustomFallback);
+ writer->write<uint8_t>(mIsDefaultFallback);
mCoverage.writeTo(writer);
// Write mCmapFmt14Coverage as a sparse array (size, non-null entry count,
// array of (index, entry))
- writer->write<uint32_t>(mCmapFmt14Coverage.size());
- uint32_t cmapFmt14CoverageEntryCount = 0;
- for (const std::unique_ptr<SparseBitSet>& coverage : mCmapFmt14Coverage) {
- if (coverage != nullptr) cmapFmt14CoverageEntryCount++;
- }
- writer->write<uint32_t>(cmapFmt14CoverageEntryCount);
- for (size_t i = 0; i < mCmapFmt14Coverage.size(); i++) {
- if (mCmapFmt14Coverage[i] != nullptr) {
- writer->write<uint32_t>(i);
- mCmapFmt14Coverage[i]->writeTo(writer);
+ writer->write<uint32_t>(mCmapFmt14CoverageCount);
+ // Skip writing the sparse entries if the size is zero
+ if (mCmapFmt14CoverageCount > 0) {
+ uint32_t cmapFmt14CoverageEntryCount = 0;
+ for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
+ if (!mCmapFmt14Coverage[i].empty()) cmapFmt14CoverageEntryCount++;
+ }
+ writer->write<uint32_t>(cmapFmt14CoverageEntryCount);
+ for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
+ if (!mCmapFmt14Coverage[i].empty()) {
+ writer->write<uint32_t>(i);
+ mCmapFmt14Coverage[i].writeTo(writer);
+ }
}
}
}
-void FontFamily::writeLocaleListInternal(BufferWriter* writer) const {
- LocaleListCache::writeTo(writer, mLocaleListId);
+// static
+std::vector<std::shared_ptr<FontFamily>> FontFamily::readVector(BufferReader* reader) {
+ // To save memory used for reference counting objects, we store
+ // Font / FontFamily in shared_ptr<vector<Font / FontFamily>>, and use
+ // shared_ptr's aliasing constructor to create shared_ptr<Font / FontFamily>
+ // that share the reference counting objects with
+ // the shared_ptr<vector<Font / FontFamily>>.
+ // We can do this because we know that all Font and FontFamily
+ // instances based on the same BufferReader share the same life span.
+ uint32_t fontsCount = reader->read<uint32_t>();
+ std::shared_ptr<std::vector<Font>> fonts = std::make_shared<std::vector<Font>>();
+ fonts->reserve(fontsCount);
+ for (uint32_t i = 0; i < fontsCount; i++) {
+ fonts->emplace_back(reader);
+ }
+ uint32_t count = reader->read<uint32_t>();
+ std::shared_ptr<std::vector<FontFamily>> families = std::make_shared<std::vector<FontFamily>>();
+ families->reserve(count);
+ std::vector<std::shared_ptr<FontFamily>> pointers;
+ pointers.reserve(count);
+ for (uint32_t i = 0; i < count; i++) {
+ // TODO(b/174672300): Revert back to emplace_back.
+ families->push_back(FontFamily(reader, fonts));
+ // Use aliasing constructor.
+ pointers.emplace_back(families, &families->back());
+ }
+ return pointers;
}
+
+// static
+void FontFamily::writeVector(BufferWriter* writer,
+ const std::vector<std::shared_ptr<FontFamily>>& families) {
+ std::vector<std::shared_ptr<Font>> fonts;
+ for (const auto& fontFamily : families) {
+ for (uint32_t i = 0; i < fontFamily->getNumFonts(); i++) {
+ fonts.emplace_back(fontFamily->getFontRef(i));
+ }
+ }
+ writer->write<uint32_t>(fonts.size());
+ for (const auto& font : fonts) {
+ font->writeTo(writer);
+ }
+ uint32_t fontIndex = 0;
+ writer->write<uint32_t>(families.size());
+ for (const auto& fontFamily : families) {
+ fontFamily->writeTo(writer, &fontIndex);
+ }
+}
+
// Compute a matching metric between two styles - 0 is an exact match
static int computeMatch(FontStyle style1, FontStyle style2) {
if (style1 == style2) return 0;
@@ -154,7 +226,7 @@ FakedFont FontFamily::getClosestMatch(FontStyle style) const {
int bestIndex = 0;
Font* bestFont = mFonts[bestIndex].get();
int bestMatch = computeMatch(bestFont->style(), style);
- for (size_t i = 1; i < mFonts.size(); i++) {
+ for (size_t i = 1; i < mFontsCount; i++) {
Font* font = mFonts[i].get();
int match = computeMatch(font->style(), style);
if (i == 0 || match < bestMatch) {
@@ -174,11 +246,31 @@ void FontFamily::computeCoverage() {
return;
}
- mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
+ std::vector<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());
+ if (mCmapFmt14CoverageCount > 0) {
+ mCmapFmt14Coverage = std::make_unique<SparseBitSet[]>(mCmapFmt14CoverageCount);
+ for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
+ mCmapFmt14Coverage[i] = std::move(cmapFmt14Coverage[i]);
+ }
+ }
- for (size_t i = 0; i < mFonts.size(); ++i) {
+ std::unordered_set<AxisTag> supportedAxesSet;
+ for (size_t i = 0; i < mFontsCount; ++i) {
std::unordered_set<AxisTag> supportedAxes = mFonts[i]->getSupportedAxes();
- mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
+ supportedAxesSet.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());
+ if (mSupportedAxesCount > 0) {
+ mSupportedAxes = sortedArrayFromSet(supportedAxesSet);
}
}
@@ -187,35 +279,36 @@ bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const
return mCoverage.get(codepoint);
}
- if (mCmapFmt14Coverage.empty()) {
+ if (mCmapFmt14CoverageCount == 0) {
return false;
}
const uint16_t vsIndex = getVsIndex(variationSelector);
- if (vsIndex >= mCmapFmt14Coverage.size()) {
+ if (vsIndex >= mCmapFmt14CoverageCount) {
// 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;
}
- const std::unique_ptr<SparseBitSet>& bitset = mCmapFmt14Coverage[vsIndex];
- if (bitset.get() == nullptr) {
+ const SparseBitSet& bitset = mCmapFmt14Coverage[vsIndex];
+ if (bitset.empty()) {
return false;
}
- return bitset->get(codepoint);
+ return bitset.get(codepoint);
}
std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
const std::vector<FontVariation>& variations) const {
- if (variations.empty() || mSupportedAxes.empty()) {
+ if (variations.empty() || mSupportedAxesCount == 0) {
return nullptr;
}
bool hasSupportedAxis = false;
for (const FontVariation& variation : variations) {
- if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
+ if (std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
+ variation.axisTag)) {
hasSupportedAxis = true;
break;
}
@@ -226,7 +319,8 @@ std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
}
std::vector<std::shared_ptr<Font>> fonts;
- for (const auto& font : mFonts) {
+ for (size_t i = 0; i < mFontsCount; i++) {
+ const std::shared_ptr<Font>& font = mFonts[i];
bool supportedVariations = false;
std::unordered_set<AxisTag> supportedAxes = font->getSupportedAxes();
if (!supportedAxes.empty()) {
@@ -248,8 +342,7 @@ std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
}
}
- return std::shared_ptr<FontFamily>(
- new FontFamily(mLocaleListId, mVariant, std::move(fonts), mIsCustomFallback));
+ return create(mLocaleListId, mVariant, std::move(fonts), mIsCustomFallback, mIsDefaultFallback);
}
} // namespace minikin
diff --git a/libs/minikin/Measurement.cpp b/libs/minikin/Measurement.cpp
index 093dba8..968ab6f 100644
--- a/libs/minikin/Measurement.cpp
+++ b/libs/minikin/Measurement.cpp
@@ -24,11 +24,33 @@
#include "minikin/BoundsCache.h"
#include "minikin/GraphemeBreak.h"
+namespace {
+bool isAsciiOrBidiControlCharacter(uint16_t c) {
+ return (0x0000 <= c && c <= 0x001F) // ASCII control characters
+ || c == 0x061C || c == 0x200E || c == 0x200F // BiDi control characters
+ || (0x202A <= c && c <= 0x202E) || (0x2066 <= c && c <= 0x2069);
+}
+
+} // namespace
+
namespace minikin {
// These could be considered helper methods of layout, but need only be loosely coupled, so
// are separate.
+/**
+ * Return the unsigned advance of the given offset from the run start.
+ *
+ * @param advances the computed advances of the characters in buf. The advance of
+ * the i-th character in buf is stored at index (i - layoutStart) in this array.
+ * @param buf the text stored in utf-16 format.
+ * @param layoutStart the start index of the character that is laid out.
+ * @param start the start index of the run.
+ * @param count the number of the characters in this run.
+ * @param offset the target offset to compute the index. It should be in the
+ * range of [start, start + count).
+ * @return the unsigned advance from the run start to the given offset.
+ */
static float getRunAdvance(const float* advances, const uint16_t* buf, size_t layoutStart,
size_t start, size_t count, size_t offset) {
float advance = 0.0f;
@@ -42,13 +64,17 @@ static float getRunAdvance(const float* advances, const uint16_t* buf, size_t la
clusterWidth = charAdvance;
}
}
- if (offset < start + count && advances[offset - layoutStart] == 0.0f) {
+ if (offset < start + count && !isAsciiOrBidiControlCharacter(buf[offset]) &&
+ advances[offset - layoutStart] == 0.0f) {
// In the middle of a cluster, distribute width of cluster so that each grapheme cluster
// gets an equal share.
// TODO: get caret information out of font when that's available
size_t nextCluster;
for (nextCluster = offset + 1; nextCluster < start + count; nextCluster++) {
- if (advances[nextCluster - layoutStart] != 0.0f) break;
+ if (advances[nextCluster - layoutStart] != 0.0f ||
+ isAsciiOrBidiControlCharacter(buf[nextCluster])) {
+ break;
+ }
}
int numGraphemeClusters = 0;
int numGraphemeClustersAfter = 0;
@@ -69,6 +95,50 @@ static float getRunAdvance(const float* advances, const uint16_t* buf, size_t la
return advance;
}
+/**
+ * Helper method that distribute the advance to ligature characters.
+ * When ligature is applied, the first character in the ligature is assigned with the entire width.
+ * This method will evenly distribute the advance to each grapheme in the ligature.
+ *
+ * @param advances the computed advances of the characters in buf. The advance of
+ * the i-th character in buf is stored at index (i - start) in this array. This
+ * method will update this array so that advances is distributed evenly for
+ * ligature characters.
+ * @param buf the text stored in utf-16 format.
+ * @param start the start index of the run.
+ * @param count the number of the characters in this run.
+ */
+void distributeAdvances(float* advances, const uint16_t* buf, size_t start, size_t count) {
+ size_t clusterStart = start;
+ while (clusterStart < start + count) {
+ float clusterAdvance = advances[clusterStart - start];
+ size_t clusterEnd;
+ for (clusterEnd = clusterStart + 1; clusterEnd < start + count; clusterEnd++) {
+ if (advances[clusterEnd - start] != 0.0f ||
+ isAsciiOrBidiControlCharacter(buf[clusterEnd])) {
+ break;
+ }
+ }
+ size_t numGraphemeClusters = 0;
+ for (size_t i = clusterStart; i < clusterEnd; i++) {
+ if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) {
+ numGraphemeClusters++;
+ }
+ }
+ // When there are more than one grapheme in this cluster, ligature is applied.
+ // And we will distribute the width to each grapheme.
+ if (numGraphemeClusters > 1) {
+ for (size_t i = clusterStart; i < clusterEnd; ++i) {
+ if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) {
+ // Only distribute the advance to the first character of the cluster.
+ advances[i - start] = clusterAdvance / numGraphemeClusters;
+ }
+ }
+ }
+ clusterStart = clusterEnd;
+ }
+}
+
float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
size_t offset) {
return getRunAdvance(advances, buf, start, start, count, offset);
diff --git a/libs/minikin/MinikinFontFactory.cpp b/libs/minikin/MinikinFontFactory.cpp
new file mode 100644
index 0000000..77f13d8
--- /dev/null
+++ b/libs/minikin/MinikinFontFactory.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include "minikin/MinikinFontFactory.h"
+
+#include <log/log.h>
+
+#include "MinikinInternal.h"
+
+namespace minikin {
+
+namespace {
+static const MinikinFontFactory* gMinikinFontFactory = nullptr;
+}
+
+MinikinFontFactory::~MinikinFontFactory() {}
+
+// static
+const MinikinFontFactory& MinikinFontFactory::getInstance() {
+ MINIKIN_ASSERT(gMinikinFontFactory != nullptr, "setInstance should have been called.");
+ return *gMinikinFontFactory;
+}
+
+// static
+void MinikinFontFactory::setInstance(const MinikinFontFactory* factory) {
+ MINIKIN_ASSERT(gMinikinFontFactory == nullptr || gMinikinFontFactory == factory,
+ "MinikinFontFactory cannot be changed after it is set.");
+ gMinikinFontFactory = factory;
+}
+
+} // namespace minikin
diff --git a/libs/minikin/MinikinInternal.h b/libs/minikin/MinikinInternal.h
index d90f099..22e1b75 100644
--- a/libs/minikin/MinikinInternal.h
+++ b/libs/minikin/MinikinInternal.h
@@ -23,6 +23,11 @@
#include <utils/Log.h>
#include <utils/Mutex.h>
+#include <algorithm>
+#include <memory>
+#include <unordered_set>
+
+#include "minikin/FontVariation.h"
#include "minikin/HbUtils.h"
#include "minikin/MinikinFont.h"
@@ -75,6 +80,14 @@ private:
HbBlobUniquePtr mBlob;
};
+template <typename T>
+std::unique_ptr<T[]> sortedArrayFromSet(const std::unordered_set<T>& set) {
+ std::unique_ptr<T[]> array(new T[set.size()]);
+ std::copy(set.begin(), set.end(), array.get());
+ std::sort(array.get(), array.get() + set.size());
+ return array;
+}
+
} // namespace minikin
#endif // MINIKIN_INTERNAL_H
diff --git a/libs/minikin/SparseBitSet.cpp b/libs/minikin/SparseBitSet.cpp
index 41151f1..a2814f7 100644
--- a/libs/minikin/SparseBitSet.cpp
+++ b/libs/minikin/SparseBitSet.cpp
@@ -54,16 +54,16 @@ void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
if (maxVal >= kMaximumCapacity) {
return;
}
- mMaxVal = maxVal;
- mIndicesCount = (mMaxVal + kPageMask) >> kLogValuesPerPage;
- // Avoid zero-filling mOwnedIndices.
- mOwnedIndices.reset(new uint16_t[mIndicesCount]);
- mIndices = mOwnedIndices.get();
+ uint32_t indicesCount = (maxVal + kPageMask) >> kLogValuesPerPage;
uint32_t nPages = calcNumPages(ranges, nRanges);
- mBitmapsCount = nPages << (kLogValuesPerPage - kLogBitsPerEl);
- mOwnedBitmaps = std::make_unique<element[]>(mBitmapsCount);
- mBitmaps = mOwnedBitmaps.get();
- mZeroPageIndex = noZeroPage;
+ uint32_t bitmapsCount = nPages << (kLogValuesPerPage - kLogBitsPerEl);
+ MappableData* data = MappableData::allocate(indicesCount, bitmapsCount);
+ mData.reset(data);
+ data->mMaxVal = maxVal;
+ uint16_t* indices = data->indices();
+ element* bitmaps = data->bitmaps();
+ memset(bitmaps, 0, sizeof(uint32_t) * bitmapsCount);
+ data->mZeroPageIndex = noZeroPage;
uint32_t nonzeroPageEnd = 0;
uint32_t currentPage = 0;
for (size_t i = 0; i < nRanges; i++) {
@@ -74,54 +74,56 @@ void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
uint32_t endPage = (end - 1) >> kLogValuesPerPage;
if (startPage >= nonzeroPageEnd) {
if (startPage > nonzeroPageEnd) {
- if (mZeroPageIndex == noZeroPage) {
- mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
+ if (data->mZeroPageIndex == noZeroPage) {
+ data->mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
}
for (uint32_t j = nonzeroPageEnd; j < startPage; j++) {
- mOwnedIndices[j] = mZeroPageIndex;
+ indices[j] = data->mZeroPageIndex;
}
}
- mOwnedIndices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
+ indices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
}
size_t index = ((currentPage - 1) << (kLogValuesPerPage - kLogBitsPerEl)) +
((start & kPageMask) >> kLogBitsPerEl);
size_t nElements = (end - (start & ~kElMask) + kElMask) >> kLogBitsPerEl;
if (nElements == 1) {
- mOwnedBitmaps[index] |=
+ bitmaps[index] |=
(kElAllOnes >> (start & kElMask)) & (kElAllOnes << ((~end + 1) & kElMask));
} else {
- mOwnedBitmaps[index] |= kElAllOnes >> (start & kElMask);
+ bitmaps[index] |= kElAllOnes >> (start & kElMask);
for (size_t j = 1; j < nElements - 1; j++) {
- mOwnedBitmaps[index + j] = kElAllOnes;
+ bitmaps[index + j] = kElAllOnes;
}
- mOwnedBitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
+ bitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
}
for (size_t j = startPage + 1; j < endPage + 1; j++) {
- mOwnedIndices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
+ indices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
}
nonzeroPageEnd = endPage + 1;
}
}
void SparseBitSet::initFromBuffer(BufferReader* reader) {
- mMaxVal = reader->read<uint32_t>();
- // mIndices and mBitmaps are not initialized when mMaxVal == 0
- if (mMaxVal == 0) return;
- std::tie(mIndices, mIndicesCount) = reader->readArray<uint16_t>();
- // element is uint32_t
- static_assert(sizeof(element) == 4);
- std::tie(mBitmaps, mBitmapsCount) = reader->readArray<element>();
- mZeroPageIndex = reader->read<uint16_t>();
+ uint32_t size = reader->read<uint32_t>();
+ if (size == 0) return;
+ mData.reset(reader->map<MappableData, alignof(MappableData)>(size));
}
void SparseBitSet::writeTo(BufferWriter* writer) const {
- writer->write<uint32_t>(mMaxVal);
- // mIndices and mBitmaps are not initialized when mMaxVal == 0
- if (mMaxVal == 0) return;
- writer->writeArray<uint16_t>(mIndices, mIndicesCount);
- writer->writeArray<element>(mBitmaps, mBitmapsCount);
- writer->write<uint16_t>(mZeroPageIndex);
+ if (mData == nullptr) {
+ // Write 0 for empty SparseBitSet.
+ writer->write<uint32_t>(0);
+ return;
+ }
+ size_t size = mData->size();
+ writer->write<uint32_t>(size);
+ static_assert(alignof(MappableData) == 4);
+ MappableData* out = writer->reserve<MappableData, alignof(MappableData)>(size);
+ if (out != nullptr) {
+ memcpy(out, mData.get(), size);
+ out->mIsMapped = 1;
+ }
}
int SparseBitSet::CountLeadingZeros(element x) {
@@ -130,11 +132,11 @@ int SparseBitSet::CountLeadingZeros(element x) {
}
uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const {
- if (fromIndex >= mMaxVal) {
+ if (mData == nullptr || fromIndex >= mData->mMaxVal) {
return kNotFound;
}
uint32_t fromPage = fromIndex >> kLogValuesPerPage;
- const element* bitmap = &mBitmaps[mIndices[fromPage]];
+ const element* bitmap = mData->bitmaps() + mData->indices()[fromPage];
uint32_t offset = (fromIndex & kPageMask) >> kLogBitsPerEl;
element e = bitmap[offset] & (kElAllOnes >> (fromIndex & kElMask));
if (e != 0) {
@@ -146,13 +148,13 @@ uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const {
return (fromIndex & ~kPageMask) + (j << kLogBitsPerEl) + CountLeadingZeros(e);
}
}
- uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage;
+ uint32_t maxPage = (mData->mMaxVal + kPageMask) >> kLogValuesPerPage;
for (uint32_t page = fromPage + 1; page < maxPage; page++) {
- uint16_t index = mIndices[page];
- if (index == mZeroPageIndex) {
+ uint16_t index = mData->indices()[page];
+ if (index == mData->mZeroPageIndex) {
continue;
}
- bitmap = &mBitmaps[index];
+ bitmap = mData->bitmaps() + index;
for (uint32_t j = 0; j < (1 << (kLogValuesPerPage - kLogBitsPerEl)); j++) {
e = bitmap[j];
if (e != 0) {
@@ -163,4 +165,15 @@ uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const {
return kNotFound;
}
+// static
+SparseBitSet::MappableData* SparseBitSet::MappableData::allocate(uint32_t indicesCount,
+ uint32_t bitmapsCount) {
+ MappableData* data = reinterpret_cast<MappableData*>(
+ malloc(MappableData::calcSize(indicesCount, bitmapsCount)));
+ data->mIndicesCount = indicesCount;
+ data->mBitmapsCount = bitmapsCount;
+ data->mIsMapped = 0;
+ return data;
+}
+
} // namespace minikin
diff --git a/libs/minikin/SystemFonts.cpp b/libs/minikin/SystemFonts.cpp
index 9c8fa66..b263d66 100644
--- a/libs/minikin/SystemFonts.cpp
+++ b/libs/minikin/SystemFonts.cpp
@@ -40,7 +40,8 @@ void SystemFonts::buildFontSetLocked() {
std::unordered_set<FontFamily*> uniqueFamilies;
for (const auto& collection : mCollections) {
- for (const auto& family : collection->getFamilies()) {
+ for (size_t i = 0; i < collection->getFamilyCount(); ++i) {
+ const auto& family = collection->getFamilyAt(i);
uniqueFamilies.insert(family.get());
}
}
diff --git a/tests/perftests/FontCollection.cpp b/tests/perftests/FontCollection.cpp
index 15d1c5e..b0be292 100644
--- a/tests/perftests/FontCollection.cpp
+++ b/tests/perftests/FontCollection.cpp
@@ -36,15 +36,14 @@ static void BM_FontCollection_construct(benchmark::State& state) {
std::vector<std::shared_ptr<FontFamily>> families =
getFontFamilies(SYSTEM_FONT_PATH, SYSTEM_FONT_XML);
while (state.KeepRunning()) {
- std::make_shared<FontCollection>(families);
+ FontCollection::create(families);
}
}
BENCHMARK(BM_FontCollection_construct);
static void BM_FontCollection_hasVariationSelector(benchmark::State& state) {
- auto collection =
- std::make_shared<FontCollection>(getFontFamilies(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
+ auto collection = FontCollection::create(getFontFamilies(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
uint32_t baseCp = state.range(0);
uint32_t vsCp = state.range(1);
@@ -80,8 +79,7 @@ struct ItemizeTestCases {
};
static void BM_FontCollection_itemize(benchmark::State& state) {
- auto collection =
- std::make_shared<FontCollection>(getFontFamilies(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
+ auto collection = FontCollection::create(getFontFamilies(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
size_t testIndex = state.range(0);
state.SetLabel("Itemize: " + ITEMIZE_TEST_CASES[testIndex].labelText);
diff --git a/tests/stresstest/FontFamilyTest.cpp b/tests/stresstest/FontFamilyTest.cpp
index 7a9813e..f5632a2 100644
--- a/tests/stresstest/FontFamilyTest.cpp
+++ b/tests/stresstest/FontFamilyTest.cpp
@@ -38,7 +38,7 @@ TEST_P(FontFamilyHarfBuzzCompatibilityTest, CoverageTest) {
auto font = std::make_shared<FreeTypeMinikinFontForTest>(fontPath);
std::vector<std::shared_ptr<Font>> fonts;
fonts.push_back(Font::Builder(font).build());
- std::shared_ptr<FontFamily> family = std::make_shared<FontFamily>(std::move(fonts));
+ std::shared_ptr<FontFamily> family = FontFamily::create(std::move(fonts));
hb_font_t* hbFont = family->getFont(0)->baseFont().get();
diff --git a/tests/unittest/BufferTest.cpp b/tests/unittest/BufferTest.cpp
index 8b1db33..2e589b2 100644
--- a/tests/unittest/BufferTest.cpp
+++ b/tests/unittest/BufferTest.cpp
@@ -33,6 +33,11 @@ public:
// padding (3), array size (4), uint32_t (4) * 2
uint32_t uint32Array[] = {0x98765432, 0x98765433};
writer->writeArray<uint32_t>(uint32Array, 2);
+ uint16_t* uint16Array = writer->reserve<uint16_t>(2 * sizeof(uint16_t));
+ if (uint16Array != nullptr) {
+ uint16Array[0] = 0x1234u;
+ uint16Array[1] = 0x5678u;
+ }
}
};
@@ -40,7 +45,7 @@ TEST(BufferTest, testMeasureWriteRead) {
TestObject testObject;
BufferWriter fakeWriter(nullptr);
testObject.writeTo(&fakeWriter);
- ASSERT_EQ(fakeWriter.size(), 20u);
+ ASSERT_EQ(fakeWriter.size(), 24u);
std::vector<uint8_t> buffer(fakeWriter.size());
BufferWriter writer(buffer.data());
@@ -48,26 +53,29 @@ TEST(BufferTest, testMeasureWriteRead) {
ASSERT_EQ(writer.size(), buffer.size());
BufferReader reader(buffer.data());
- ASSERT_EQ(reader.data(), buffer.data());
- ASSERT_EQ(reader.pos(), 0u);
+ ASSERT_EQ(reader.current(), buffer.data());
ASSERT_EQ(reader.read<uint8_t>(), 0xABu);
- ASSERT_EQ(reader.pos(), 1u);
+ ASSERT_EQ(reader.current(), buffer.data() + 1u);
ASSERT_EQ(reader.read<uint16_t>(), 0xCDEFu);
- ASSERT_EQ(reader.pos(), 4u);
+ ASSERT_EQ(reader.current(), buffer.data() + 4u);
ASSERT_EQ(reader.read<uint8_t>(), 0x01u);
- ASSERT_EQ(reader.pos(), 5u);
+ ASSERT_EQ(reader.current(), buffer.data() + 5u);
auto [uint32Array, size] = reader.readArray<uint32_t>();
ASSERT_EQ(size, 2u);
ASSERT_EQ(uint32Array[0], 0x98765432u);
ASSERT_EQ(uint32Array[1], 0x98765433u);
- ASSERT_EQ(reader.pos(), 20u);
+ ASSERT_EQ(reader.current(), buffer.data() + 20u);
+ const uint16_t* uint16Array = reader.map<uint16_t>(4);
+ ASSERT_EQ(uint16Array[0], 0x1234u);
+ ASSERT_EQ(uint16Array[1], 0x5678u);
+ ASSERT_EQ(reader.current(), buffer.data() + 24u);
}
TEST(BufferTest, testSkip) {
TestObject testObject;
BufferWriter fakeWriter(nullptr);
testObject.writeTo(&fakeWriter);
- ASSERT_EQ(fakeWriter.size(), 20u);
+ ASSERT_EQ(fakeWriter.size(), 24u);
std::vector<uint8_t> buffer(fakeWriter.size());
BufferWriter writer(buffer.data());
@@ -75,16 +83,18 @@ TEST(BufferTest, testSkip) {
ASSERT_EQ(writer.size(), buffer.size());
BufferReader reader(buffer.data());
- ASSERT_EQ(reader.data(), buffer.data());
- ASSERT_EQ(reader.pos(), 0u);
+ ASSERT_EQ(reader.current(), buffer.data());
reader.skip<uint8_t>();
- ASSERT_EQ(reader.pos(), 1u);
+ ASSERT_EQ(reader.current(), buffer.data() + 1u);
reader.read<uint16_t>();
- ASSERT_EQ(reader.pos(), 4u);
+ ASSERT_EQ(reader.current(), buffer.data() + 4u);
reader.skip<uint8_t>();
- ASSERT_EQ(reader.pos(), 5u);
+ ASSERT_EQ(reader.current(), buffer.data() + 5u);
reader.skipArray<uint32_t>();
- ASSERT_EQ(reader.pos(), 20u);
+ ASSERT_EQ(reader.current(), buffer.data() + 20u);
+ // No skip function for mapped data.
+ reader.map<uint16_t>(4);
+ ASSERT_EQ(reader.current(), buffer.data() + 24u);
}
} // namespace minikin
diff --git a/tests/unittest/CmapCoverageTest.cpp b/tests/unittest/CmapCoverageTest.cpp
index 9dba583..f871c19 100644
--- a/tests/unittest/CmapCoverageTest.cpp
+++ b/tests/unittest/CmapCoverageTest.cpp
@@ -285,7 +285,7 @@ private:
};
TEST(CmapCoverageTest, SingleFormat4_brokenCmap) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
{
SCOPED_TRACE("Reading beyond buffer size - Too small cmap size");
std::vector<uint8_t> cmap =
@@ -337,7 +337,7 @@ TEST(CmapCoverageTest, SingleFormat4_brokenCmap) {
}
TEST(CmapCoverageTest, SingleFormat4) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
struct TestCast {
std::string testTitle;
uint16_t platformId;
@@ -360,7 +360,7 @@ TEST(CmapCoverageTest, SingleFormat4) {
}
TEST(CmapCoverageTest, SingleFormat12) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
struct TestCast {
std::string testTitle;
@@ -384,7 +384,7 @@ TEST(CmapCoverageTest, SingleFormat12) {
}
TEST(CmapCoverageTest, Format12_beyondTheUnicodeLimit) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
{
SCOPED_TRACE("Starting range is out of Unicode code point. Should be ignored.");
std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
@@ -410,7 +410,7 @@ TEST(CmapCoverageTest, Format12_beyondTheUnicodeLimit) {
}
TEST(CmapCoverageTest, notSupportedEncodings) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
struct TestCast {
std::string testTitle;
@@ -451,7 +451,7 @@ TEST(CmapCoverageTest, notSupportedEncodings) {
}
TEST(CmapCoverageTest, brokenFormat4Table) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
{
SCOPED_TRACE("Too small table cmap size");
std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
@@ -505,7 +505,7 @@ TEST(CmapCoverageTest, brokenFormat4Table) {
}
TEST(CmapCoverageTest, duplicatedCmap4EntryTest) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'b', 'b', 'b'}));
CmapBuilder builder(1);
builder.appendTable(0, 0, table);
@@ -518,7 +518,7 @@ TEST(CmapCoverageTest, duplicatedCmap4EntryTest) {
}
TEST(CmapCoverageTest, brokenFormat12Table) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
{
SCOPED_TRACE("Too small cmap size");
std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
@@ -594,7 +594,7 @@ TEST(CmapCoverageTest, TableSelection_Priority) {
std::vector<uint8_t> format4 = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
std::vector<uint8_t> format12 = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
{
SCOPED_TRACE("(platform, encoding) = (3, 10) is the highest priority.");
@@ -646,7 +646,7 @@ TEST(CmapCoverageTest, TableSelection_Priority) {
TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat4Table) {
std::vector<uint8_t> validTable = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
{
SCOPED_TRACE("Unsupported format");
CmapBuilder builder(2);
@@ -692,7 +692,7 @@ TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat4Table) {
}
TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat12Table) {
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
std::vector<uint8_t> validTable = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
{
SCOPED_TRACE("Unsupported format");
@@ -751,28 +751,28 @@ TEST(CmapCoverageTest, TableSelection_VSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
EXPECT_TRUE(coverage.get('a'));
ASSERT_FALSE(vsTables.empty());
const uint16_t vs15Index = getVsIndex(0xFE0E);
ASSERT_LT(vs15Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs15Index]);
- EXPECT_TRUE(vsTables[vs15Index]->get('a'));
- EXPECT_TRUE(vsTables[vs15Index]->get('b'));
+ ASSERT_FALSE(vsTables[vs15Index].empty());
+ EXPECT_TRUE(vsTables[vs15Index].get('a'));
+ EXPECT_TRUE(vsTables[vs15Index].get('b'));
const uint16_t vs16Index = getVsIndex(0xFE0F);
ASSERT_LT(vs16Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs16Index]);
- EXPECT_TRUE(vsTables[vs16Index]->get('a'));
- EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+ ASSERT_FALSE(vsTables[vs16Index].empty());
+ EXPECT_TRUE(vsTables[vs16Index].get('a'));
+ EXPECT_TRUE(vsTables[vs16Index].get('b'));
const uint16_t vs17Index = getVsIndex(0xE0100);
ASSERT_LT(vs17Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs17Index]);
- EXPECT_TRUE(vsTables[vs17Index]->get('a'));
- EXPECT_TRUE(vsTables[vs17Index]->get('b'));
+ ASSERT_FALSE(vsTables[vs17Index].empty());
+ EXPECT_TRUE(vsTables[vs17Index].get('a'));
+ EXPECT_TRUE(vsTables[vs17Index].get('b'));
}
TEST(CmapCoverageTest, TableSelection_InterSection) {
@@ -799,66 +799,66 @@ TEST(CmapCoverageTest, TableSelection_InterSection) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
EXPECT_TRUE(coverage.get('a'));
ASSERT_FALSE(vsTables.empty());
const uint16_t vs15Index = getVsIndex(0xFE0E);
ASSERT_LT(vs15Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs15Index]);
- EXPECT_TRUE(vsTables[vs15Index]->get('a'));
- EXPECT_TRUE(vsTables[vs15Index]->get('b'));
- EXPECT_TRUE(vsTables[vs15Index]->get('c'));
- EXPECT_TRUE(vsTables[vs15Index]->get('d'));
- EXPECT_TRUE(vsTables[vs15Index]->get('e'));
+ ASSERT_FALSE(vsTables[vs15Index].empty());
+ EXPECT_TRUE(vsTables[vs15Index].get('a'));
+ EXPECT_TRUE(vsTables[vs15Index].get('b'));
+ EXPECT_TRUE(vsTables[vs15Index].get('c'));
+ EXPECT_TRUE(vsTables[vs15Index].get('d'));
+ EXPECT_TRUE(vsTables[vs15Index].get('e'));
const uint16_t vs16Index = getVsIndex(0xFE0F);
ASSERT_LT(vs16Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs16Index]);
- EXPECT_TRUE(vsTables[vs16Index]->get('a'));
- EXPECT_TRUE(vsTables[vs16Index]->get('b'));
- EXPECT_TRUE(vsTables[vs16Index]->get('c'));
- EXPECT_TRUE(vsTables[vs16Index]->get('d'));
- EXPECT_TRUE(vsTables[vs16Index]->get('e'));
+ ASSERT_FALSE(vsTables[vs16Index].empty());
+ EXPECT_TRUE(vsTables[vs16Index].get('a'));
+ EXPECT_TRUE(vsTables[vs16Index].get('b'));
+ EXPECT_TRUE(vsTables[vs16Index].get('c'));
+ EXPECT_TRUE(vsTables[vs16Index].get('d'));
+ EXPECT_TRUE(vsTables[vs16Index].get('e'));
const uint16_t vs17Index = getVsIndex(0xE0100);
ASSERT_LT(vs17Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs17Index]);
- EXPECT_TRUE(vsTables[vs17Index]->get('a'));
- EXPECT_TRUE(vsTables[vs17Index]->get('b'));
- EXPECT_TRUE(vsTables[vs17Index]->get('c'));
- EXPECT_TRUE(vsTables[vs17Index]->get('d'));
+ ASSERT_FALSE(vsTables[vs17Index].empty());
+ EXPECT_TRUE(vsTables[vs17Index].get('a'));
+ EXPECT_TRUE(vsTables[vs17Index].get('b'));
+ EXPECT_TRUE(vsTables[vs17Index].get('c'));
+ EXPECT_TRUE(vsTables[vs17Index].get('d'));
const uint16_t vs18Index = getVsIndex(0xE0101);
ASSERT_LT(vs18Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs18Index]);
- EXPECT_TRUE(vsTables[vs18Index]->get('a'));
- EXPECT_TRUE(vsTables[vs18Index]->get('b'));
- EXPECT_TRUE(vsTables[vs18Index]->get('c'));
- EXPECT_TRUE(vsTables[vs18Index]->get('d'));
+ ASSERT_FALSE(vsTables[vs18Index].empty());
+ EXPECT_TRUE(vsTables[vs18Index].get('a'));
+ EXPECT_TRUE(vsTables[vs18Index].get('b'));
+ EXPECT_TRUE(vsTables[vs18Index].get('c'));
+ EXPECT_TRUE(vsTables[vs18Index].get('d'));
const uint16_t vs19Index = getVsIndex(0xE0102);
ASSERT_LT(vs19Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs19Index]);
- EXPECT_TRUE(vsTables[vs19Index]->get('a'));
- EXPECT_TRUE(vsTables[vs19Index]->get('b'));
- EXPECT_TRUE(vsTables[vs19Index]->get('c'));
- EXPECT_TRUE(vsTables[vs19Index]->get('d'));
- EXPECT_TRUE(vsTables[vs19Index]->get('e'));
- EXPECT_TRUE(vsTables[vs19Index]->get('f'));
- EXPECT_TRUE(vsTables[vs19Index]->get('g'));
- EXPECT_TRUE(vsTables[vs19Index]->get('h'));
+ ASSERT_FALSE(vsTables[vs19Index].empty());
+ EXPECT_TRUE(vsTables[vs19Index].get('a'));
+ EXPECT_TRUE(vsTables[vs19Index].get('b'));
+ EXPECT_TRUE(vsTables[vs19Index].get('c'));
+ EXPECT_TRUE(vsTables[vs19Index].get('d'));
+ EXPECT_TRUE(vsTables[vs19Index].get('e'));
+ EXPECT_TRUE(vsTables[vs19Index].get('f'));
+ EXPECT_TRUE(vsTables[vs19Index].get('g'));
+ EXPECT_TRUE(vsTables[vs19Index].get('h'));
const uint16_t vs20Index = getVsIndex(0xE0103);
ASSERT_LT(vs20Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs20Index]);
- EXPECT_TRUE(vsTables[vs20Index]->get('a'));
- EXPECT_TRUE(vsTables[vs20Index]->get('b'));
- EXPECT_TRUE(vsTables[vs20Index]->get('c'));
- EXPECT_TRUE(vsTables[vs20Index]->get('d'));
- EXPECT_TRUE(vsTables[vs20Index]->get('e'));
- EXPECT_TRUE(vsTables[vs20Index]->get('f'));
+ ASSERT_FALSE(vsTables[vs20Index].empty());
+ EXPECT_TRUE(vsTables[vs20Index].get('a'));
+ EXPECT_TRUE(vsTables[vs20Index].get('b'));
+ EXPECT_TRUE(vsTables[vs20Index].get('c'));
+ EXPECT_TRUE(vsTables[vs20Index].get('d'));
+ EXPECT_TRUE(vsTables[vs20Index].get('e'));
+ EXPECT_TRUE(vsTables[vs20Index].get('f'));
}
TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
@@ -872,7 +872,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage =
CmapCoverage::getCoverage(cmap.data(), 3 /* too small size */, &vsTables);
EXPECT_FALSE(coverage.get('a'));
@@ -888,7 +888,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -902,7 +902,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -916,7 +916,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -931,7 +931,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -946,7 +946,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -959,7 +959,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -972,7 +972,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -985,7 +985,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -998,7 +998,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -1014,7 +1014,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -1030,7 +1030,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -1046,7 +1046,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -1062,7 +1062,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -1078,7 +1078,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -1094,7 +1094,7 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
ASSERT_TRUE(vsTables.empty());
}
@@ -1113,17 +1113,17 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable_bestEffort) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
const uint16_t vs16Index = getVsIndex(0xFE0F);
ASSERT_LT(vs16Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs16Index]);
- EXPECT_TRUE(vsTables[vs16Index]->get('a'));
- EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+ ASSERT_FALSE(vsTables[vs16Index].empty());
+ EXPECT_TRUE(vsTables[vs16Index].get('a'));
+ EXPECT_TRUE(vsTables[vs16Index].get('b'));
const uint16_t vs15Index = getVsIndex(0xFE0E);
- EXPECT_FALSE(vsTables[vs15Index]);
+ EXPECT_TRUE(vsTables[vs15Index].empty());
}
{
SCOPED_TRACE("Invalid non default UVS offset in variation records");
@@ -1136,17 +1136,17 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable_bestEffort) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
const uint16_t vs16Index = getVsIndex(0xFE0F);
ASSERT_LT(vs16Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs16Index]);
- EXPECT_TRUE(vsTables[vs16Index]->get('a'));
- EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+ ASSERT_FALSE(vsTables[vs16Index].empty());
+ EXPECT_TRUE(vsTables[vs16Index].get('a'));
+ EXPECT_TRUE(vsTables[vs16Index].get('b'));
const uint16_t vs15Index = getVsIndex(0xFE0E);
- EXPECT_FALSE(vsTables[vs15Index]);
+ EXPECT_TRUE(vsTables[vs15Index].empty());
}
{
SCOPED_TRACE("Unknown variation selectors.");
@@ -1158,14 +1158,14 @@ TEST(CmapCoverageTest, TableSelection_brokenVSTable_bestEffort) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
const uint16_t vs16Index = getVsIndex(0xFE0F);
ASSERT_LT(vs16Index, vsTables.size());
- ASSERT_TRUE(vsTables[vs16Index]);
- EXPECT_TRUE(vsTables[vs16Index]->get('a'));
- EXPECT_TRUE(vsTables[vs16Index]->get('b'));
+ ASSERT_FALSE(vsTables[vs16Index].empty());
+ EXPECT_TRUE(vsTables[vs16Index].get('a'));
+ EXPECT_TRUE(vsTables[vs16Index].get('b'));
}
}
@@ -1183,16 +1183,16 @@ TEST(CmapCoverageTest, TableSelection_defaultUVSPointMissingGlyph) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
const uint16_t vsIndex = getVsIndex(0xFE0F);
ASSERT_LT(vsIndex, vsTables.size());
- ASSERT_TRUE(vsTables[vsIndex]);
+ ASSERT_FALSE(vsTables[vsIndex].empty());
for (char c = 'a'; c <= 'z'; ++c) {
// Default UVS table points the variation sequence to the glyph of the base code point.
// Thus, if the base code point is not supported, we should exclude them.
- EXPECT_EQ(coverage.get(c), vsTables[vsIndex]->get(c)) << c;
+ EXPECT_EQ(coverage.get(c), vsTables[vsIndex].get(c)) << c;
}
}
@@ -1206,11 +1206,11 @@ TEST(CmapCoverageTest, TableSelection_vsTableOnly) {
builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
std::vector<uint8_t> cmap = builder.build();
- std::vector<std::unique_ptr<SparseBitSet>> vsTables;
+ std::vector<SparseBitSet> vsTables;
SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
const uint16_t vsIndex = getVsIndex(0xFE0F);
ASSERT_LT(vsIndex, vsTables.size());
- ASSERT_TRUE(vsTables[vsIndex]);
- EXPECT_TRUE(vsTables[vsIndex]->get('a'));
+ ASSERT_FALSE(vsTables[vsIndex].empty());
+ EXPECT_TRUE(vsTables[vsIndex].get('a'));
}
} // namespace minikin
diff --git a/tests/unittest/FontCollectionItemizeTest.cpp b/tests/unittest/FontCollectionItemizeTest.cpp
index e3f8a6b..1723519 100644
--- a/tests/unittest/FontCollectionItemizeTest.cpp
+++ b/tests/unittest/FontCollectionItemizeTest.cpp
@@ -741,7 +741,7 @@ TEST(FontCollectionItemizeTest, itemize_vs_sequence_but_no_base_char) {
families.push_back(buildFontFamily(kLatinFont));
families.push_back(buildFontFamily(kVSTestFont));
- std::shared_ptr<FontCollection> collection(new FontCollection(families));
+ std::shared_ptr<FontCollection> collection(FontCollection::create(families));
auto runs = itemize(collection, "U+717D U+FE02");
ASSERT_EQ(1U, runs.size());
@@ -937,9 +937,9 @@ TEST(FontCollectionItemizeTest, itemize_LocaleScore) {
std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(kNoGlyphFont));
std::vector<std::shared_ptr<Font>> fonts;
fonts.push_back(Font::Builder(firstFamilyMinikinFont).build());
- auto firstFamily =
- std::make_shared<FontFamily>(registerLocaleList("und"), FamilyVariant::DEFAULT,
- std::move(fonts), false /* isCustomFallback */);
+ auto firstFamily = FontFamily::create(registerLocaleList("und"), FamilyVariant::DEFAULT,
+ std::move(fonts), false /* isCustomFallback */,
+ false /* isDefaultFallback */);
families.push_back(firstFamily);
// Prepare font families
@@ -952,13 +952,13 @@ TEST(FontCollectionItemizeTest, itemize_LocaleScore) {
std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(kJAFont));
std::vector<std::shared_ptr<Font>> fonts;
fonts.push_back(Font::Builder(minikinFont).build());
- auto family = std::make_shared<FontFamily>(registerLocaleList(testCase.fontLocales[i]),
- FamilyVariant::DEFAULT, std::move(fonts),
- false /* isCustomFallback */);
+ auto family = FontFamily::create(
+ registerLocaleList(testCase.fontLocales[i]), FamilyVariant::DEFAULT,
+ std::move(fonts), false /* isCustomFallback */, false /* isDefaultFallback */);
families.push_back(family);
fontLocaleIdxMap.insert(std::make_pair(minikinFont.get(), i));
}
- std::shared_ptr<FontCollection> collection(new FontCollection(families));
+ std::shared_ptr<FontCollection> collection(FontCollection::create(families));
// Do itemize
auto runs = itemize(collection, "U+9AA8", testCase.userPreferredLocale);
ASSERT_EQ(1U, runs.size());
@@ -1525,8 +1525,8 @@ TEST(FontCollectionItemizeTest, itemizeShouldKeepOrderForVS) {
std::vector<std::shared_ptr<FontFamily>> families = {dummyFamily, familyA, familyB};
std::vector<std::shared_ptr<FontFamily>> reversedFamilies = {dummyFamily, familyB, familyA};
- std::shared_ptr<FontCollection> collection(new FontCollection(families));
- std::shared_ptr<FontCollection> reversedCollection(new FontCollection(reversedFamilies));
+ std::shared_ptr<FontCollection> collection(FontCollection::create(families));
+ std::shared_ptr<FontCollection> reversedCollection(FontCollection::create(reversedFamilies));
// Both fontA/fontB support U+35A8 but don't support U+35A8 U+E0100. The first font should be
// selected.
@@ -1548,8 +1548,8 @@ TEST(FontCollectionItemizeTest, itemizeShouldKeepOrderForVS2) {
std::vector<std::shared_ptr<FontFamily>> reversedFamilies = {dummyFamily, noCmapFormat14Family,
hasCmapFormat14Family};
- std::shared_ptr<FontCollection> collection(new FontCollection(families));
- std::shared_ptr<FontCollection> reversedCollection(new FontCollection(reversedFamilies));
+ std::shared_ptr<FontCollection> collection(FontCollection::create(families));
+ std::shared_ptr<FontCollection> reversedCollection(FontCollection::create(reversedFamilies));
// Both hasCmapFormat14Font/noCmapFormat14Font support U+5380 but don't support U+5380 U+E0100.
// The first font should be selected.
@@ -1567,7 +1567,7 @@ TEST(FontCollectionItemizeTest, colorEmojiSelectionTest) {
std::vector<std::shared_ptr<FontFamily>> families = {dummyFamily, textEmojiFamily,
colorEmojiFamily};
- auto collection = std::make_shared<FontCollection>(families);
+ auto collection = FontCollection::create(families);
// Both textEmojiFamily and colorEmojiFamily supports U+203C and U+23E9.
// U+203C is text default emoji, and U+23E9 is color default emoji.
auto runs = itemize(collection, "U+203C", "en-US,en-Zsym");
@@ -1619,7 +1619,7 @@ TEST(FontCollectionItemizeTest, customFallbackTest) {
std::vector<std::shared_ptr<FontFamily>> families = {firstFamily, customFallbackFamily,
languageFamily};
- auto collection = std::make_shared<FontCollection>(families);
+ auto collection = FontCollection::create(families);
auto runs = itemize(collection, "'a'", "");
EXPECT_EQ(customFallbackFamily->getFont(0), runs[0].fakedFont.font.get());
@@ -1643,7 +1643,7 @@ std::vector<ItemizeResult> itemizeEmojiAndFontPostScriptNames(const std::string&
std::vector<std::shared_ptr<FontFamily>> families = {firstFamily, OverrideEmojiFamily,
emojiBaseFamily};
- auto collection = std::make_shared<FontCollection>(families);
+ auto collection = FontCollection::create(families);
auto runs = itemize(collection, txt.c_str());
std::vector<ItemizeResult> out;
diff --git a/tests/unittest/FontCollectionTest.cpp b/tests/unittest/FontCollectionTest.cpp
index aa9d4a8..b4933ab 100644
--- a/tests/unittest/FontCollectionTest.cpp
+++ b/tests/unittest/FontCollectionTest.cpp
@@ -177,25 +177,40 @@ TEST(FontCollectionTest, createWithVariations) {
}
}
+TEST(FontCollectionTest, createCollectionWithFamilies) {
+ auto fallback = buildFontCollectionFromXml(kEmojiXmlFile);
+ std::shared_ptr<FontFamily> family = buildFontFamily(kVsTestFont);
+ std::shared_ptr<FontCollection> created = fallback->createCollectionWithFamilies({family});
+ ASSERT_EQ(fallback->getFamilyCount() + 1, created->getFamilyCount());
+ EXPECT_EQ(family, created->getFamilyAt(0));
+ for (size_t i = 0; i < fallback->getFamilyCount(); i++) {
+ EXPECT_EQ(fallback->getFamilyAt(i), created->getFamilyAt(i + 1));
+ }
+}
+
std::vector<uint8_t> writeToBuffer(
const std::vector<std::shared_ptr<FontCollection>>& collections) {
BufferWriter fakeWriter(nullptr);
- FontCollection::writeVector<writeFreeTypeMinikinFontForTest>(&fakeWriter, collections);
+ FontCollection::writeVector(&fakeWriter, collections);
std::vector<uint8_t> buffer(fakeWriter.size());
BufferWriter writer(buffer.data());
- FontCollection::writeVector<writeFreeTypeMinikinFontForTest>(&writer, collections);
+ FontCollection::writeVector(&writer, collections);
return buffer;
}
TEST(FontCollectionTest, bufferTest) {
+ FreeTypeMinikinFontForTestFactory::init();
{
std::vector<std::shared_ptr<FontCollection>> original({buildFontCollection(kVsTestFont)});
std::vector<uint8_t> buffer = writeToBuffer(original);
BufferReader reader(buffer.data());
- auto copied = FontCollection::readVector<readFreeTypeMinikinFontForTest>(&reader);
+ auto copied = FontCollection::readVector(&reader);
EXPECT_EQ(1u, copied.size());
expectVSGlyphsForVsTestFont(copied[0].get());
- EXPECT_EQ(original[0]->getSupportedTags(), copied[0]->getSupportedTags());
+ ASSERT_EQ(original[0]->getSupportedAxesCount(), copied[0]->getSupportedAxesCount());
+ for (size_t i = 0; i < original[0]->getSupportedAxesCount(); i++) {
+ EXPECT_EQ(original[0]->getSupportedAxisAt(i), copied[0]->getSupportedAxisAt(i));
+ }
// Id will be different.
EXPECT_NE(original[0]->getId(), copied[0]->getId());
std::vector<uint8_t> newBuffer = writeToBuffer(copied);
@@ -204,14 +219,14 @@ TEST(FontCollectionTest, bufferTest) {
{
// Test that FontFamily instances are shared.
std::vector<std::shared_ptr<FontFamily>> families = {buildFontFamily(kVsTestFont)};
- auto fc1 = std::make_shared<FontCollection>(families);
- auto fc2 = std::make_shared<FontCollection>(families);
+ auto fc1 = FontCollection::create(families);
+ auto fc2 = FontCollection::create(families);
std::vector<std::shared_ptr<FontCollection>> original({fc1, fc2});
std::vector<uint8_t> buffer = writeToBuffer(original);
BufferReader reader(buffer.data());
- auto copied = FontCollection::readVector<readFreeTypeMinikinFontForTest>(&reader);
+ auto copied = FontCollection::readVector(&reader);
EXPECT_EQ(2u, copied.size());
- EXPECT_EQ(copied[0]->mFamilies[0], copied[1]->mFamilies[0]);
+ EXPECT_EQ(copied[0]->getFamilyAt(0), copied[1]->getFamilyAt(0));
std::vector<uint8_t> newBuffer = writeToBuffer(copied);
EXPECT_EQ(buffer, newBuffer);
}
@@ -223,12 +238,12 @@ TEST(FontCollectionTest, bufferTest) {
{buildFontCollection(kMultiAxisFont)});
std::vector<uint8_t> buffer = writeToBuffer(original);
BufferReader reader(buffer.data());
- auto copied = FontCollection::readVector<readFreeTypeMinikinFontForTest>(&reader);
+ auto copied = FontCollection::readVector(&reader);
EXPECT_EQ(1u, copied.size());
- EXPECT_EQ(1u,
- copied[0]->getSupportedTags().count(MinikinFont::MakeTag('w', 'd', 't', 'h')));
- EXPECT_EQ(1u,
- copied[0]->getSupportedTags().count(MinikinFont::MakeTag('w', 'g', 'h', 't')));
+ ASSERT_EQ(2u, copied[0]->getSupportedAxesCount());
+ // mSupportedAxes must be sorted.
+ EXPECT_EQ(MinikinFont::MakeTag('w', 'd', 't', 'h'), copied[0]->getSupportedAxisAt(0));
+ EXPECT_EQ(MinikinFont::MakeTag('w', 'g', 'h', 't'), copied[0]->getSupportedAxisAt(1));
std::vector<uint8_t> newBuffer = writeToBuffer(copied);
EXPECT_EQ(buffer, newBuffer);
}
diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp
index ce710f3..3230c27 100644
--- a/tests/unittest/FontFamilyTest.cpp
+++ b/tests/unittest/FontFamilyTest.cpp
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-#include "minikin/FontFamily.h"
-
#include <gtest/gtest.h>
-
-#include "minikin/LocaleList.h"
+#include <malloc.h>
#include "BufferUtils.h"
#include "FontTestUtils.h"
#include "FreeTypeMinikinFontForTest.h"
#include "LocaleListCache.h"
#include "MinikinInternal.h"
+#include "minikin/FontFamily.h"
+#include "minikin/LocaleList.h"
namespace minikin {
@@ -801,8 +800,8 @@ TEST_F(FontFamilyTest, closestMatch) {
fonts.push_back(Font::Builder(dummyFont).setStyle(familyStyle).build());
}
- FontFamily family(std::move(fonts));
- FakedFont closest = family.getClosestMatch(testCase.wantedStyle);
+ std::shared_ptr<FontFamily> family = FontFamily::create(std::move(fonts));
+ FakedFont closest = family->getClosestMatch(testCase.wantedStyle);
size_t idx = dummyFonts.size();
for (size_t i = 0; i < dummyFonts.size(); i++) {
@@ -821,41 +820,57 @@ TEST_F(FontFamilyTest, closestMatch) {
}
}
-TEST_F(FontFamilyTest, bufferTest) {
- {
- // Font with variation selectors
- std::shared_ptr<FontFamily> original = buildFontFamily(kVsTestFont);
- std::vector<uint8_t> buffer =
- writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*original);
- BufferReader reader(buffer.data());
- std::shared_ptr<FontFamily> copied =
- FontFamily::readFrom<readFreeTypeMinikinFontForTest>(&reader);
- ASSERT_EQ(original->localeListId(), copied->localeListId());
- ASSERT_EQ(original->variant(), copied->variant());
- ASSERT_EQ(original->getNumFonts(), copied->getNumFonts());
- ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
- ASSERT_EQ(original->isColorEmojiFamily(), copied->isColorEmojiFamily());
- ASSERT_EQ(original->isCustomFallback(), copied->isCustomFallback());
- ASSERT_EQ(original->hasVSTable(), copied->hasVSTable());
- expectVSGlyphsForVsTestFont(copied.get());
- std::vector<uint8_t> newBuffer =
- writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*copied);
- ASSERT_EQ(buffer, newBuffer);
+std::vector<uint8_t> writeToBuffer(const std::vector<std::shared_ptr<FontFamily>>& families) {
+ BufferWriter fakeWriter(nullptr);
+ FontFamily::writeVector(&fakeWriter, families);
+ std::vector<uint8_t> buffer(fakeWriter.size());
+ BufferWriter writer(buffer.data());
+ FontFamily::writeVector(&writer, families);
+ return buffer;
+}
+
+void expectFontFamilyEquals(const std::shared_ptr<FontFamily>& expected,
+ const std::shared_ptr<FontFamily>& actual) {
+ ASSERT_EQ(expected->localeListId(), actual->localeListId());
+ ASSERT_EQ(expected->variant(), actual->variant());
+ ASSERT_EQ(expected->getNumFonts(), actual->getNumFonts());
+ ASSERT_EQ(expected->getSupportedAxesCount(), actual->getSupportedAxesCount());
+ for (size_t i = 0; i < expected->getSupportedAxesCount(); i++) {
+ ASSERT_EQ(expected->getSupportedAxisAt(i), actual->getSupportedAxisAt(i));
}
+ ASSERT_EQ(expected->isColorEmojiFamily(), actual->isColorEmojiFamily());
+ ASSERT_EQ(expected->isCustomFallback(), actual->isCustomFallback());
+ ASSERT_EQ(expected->hasVSTable(), actual->hasVSTable());
+}
+
+size_t getHeapSize() {
+ struct mallinfo info = mallinfo();
+ return info.uordblks;
+}
+
+TEST_F(FontFamilyTest, bufferTest) {
+ FreeTypeMinikinFontForTestFactory::init();
+ size_t baseHeapSize = getHeapSize();
{
- // Font with axes
constexpr char kMultiAxisFont[] = "MultiAxis.ttf";
- std::shared_ptr<FontFamily> original = buildFontFamily(kMultiAxisFont);
- std::vector<uint8_t> buffer =
- writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*original);
+ std::vector<std::shared_ptr<FontFamily>> original = {
+ // Font with variation selectors
+ buildFontFamily(kVsTestFont),
+ // Font with axes
+ buildFontFamily(kMultiAxisFont),
+ };
+ std::vector<uint8_t> buffer = writeToBuffer(original);
BufferReader reader(buffer.data());
- std::shared_ptr<FontFamily> copied =
- FontFamily::readFrom<readFreeTypeMinikinFontForTest>(&reader);
- ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
- std::vector<uint8_t> newBuffer =
- writeToBuffer<FontFamily, writeFreeTypeMinikinFontForTest>(*copied);
+ std::vector<std::shared_ptr<FontFamily>> copied = FontFamily::readVector(&reader);
+ ASSERT_EQ(2u, copied.size());
+ expectFontFamilyEquals(original[0], copied[0]);
+ expectVSGlyphsForVsTestFont(copied[0].get());
+ expectFontFamilyEquals(original[1], copied[1]);
+ std::vector<uint8_t> newBuffer = writeToBuffer(copied);
ASSERT_EQ(buffer, newBuffer);
}
+ // Test that there is no leak after all FontFamily is destructed.
+ EXPECT_EQ(baseHeapSize, getHeapSize());
}
} // namespace minikin
diff --git a/tests/unittest/FontTest.cpp b/tests/unittest/FontTest.cpp
index 68f5b51..b50ac90 100644
--- a/tests/unittest/FontTest.cpp
+++ b/tests/unittest/FontTest.cpp
@@ -24,19 +24,121 @@
namespace minikin {
+namespace {
+
+size_t getHeapSize() {
+ struct mallinfo info = mallinfo();
+ return info.uordblks;
+}
+
+} // namespace
+
TEST(FontTest, BufferTest) {
+ FreeTypeMinikinFontForTestFactory::init();
auto minikinFont = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath("Ascii.ttf"));
std::shared_ptr<Font> original = Font::Builder(minikinFont).build();
- std::vector<uint8_t> buffer = writeToBuffer<Font, writeFreeTypeMinikinFontForTest>(*original);
+ std::vector<uint8_t> buffer = writeToBuffer<Font>(*original);
BufferReader reader(buffer.data());
- std::shared_ptr<Font> font =
- Font::readFrom<readFreeTypeMinikinFontForTest>(&reader, kEmptyLocaleListId);
- EXPECT_EQ(minikinFont->GetFontPath(), font->typeface()->GetFontPath());
- EXPECT_EQ(original->style(), font->style());
- EXPECT_NE(nullptr, font->baseFont());
- std::vector<uint8_t> newBuffer = writeToBuffer<Font, writeFreeTypeMinikinFontForTest>(*font);
+ Font font(&reader);
+ EXPECT_EQ(minikinFont->GetFontPath(), font.typeface()->GetFontPath());
+ EXPECT_EQ(original->style(), font.style());
+ EXPECT_EQ(original->getLocaleListId(), font.getLocaleListId());
+ // baseFont() should return the same non-null instance when called twice.
+ const auto& baseFont = font.baseFont();
+ EXPECT_NE(nullptr, baseFont);
+ EXPECT_EQ(baseFont, font.baseFont());
+ // typeface() should return the same non-null instance when called twice.
+ const auto& typeface = font.typeface();
+ EXPECT_NE(nullptr, typeface);
+ EXPECT_EQ(typeface, font.typeface());
+ std::vector<uint8_t> newBuffer = writeToBuffer<Font>(font);
EXPECT_EQ(buffer, newBuffer);
}
+TEST(FontTest, MoveConstructorTest) {
+ FreeTypeMinikinFontForTestFactory::init();
+ // Note: by definition, only BufferReader-based Font can be moved.
+ auto minikinFont = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath("Ascii.ttf"));
+ std::shared_ptr<Font> original = Font::Builder(minikinFont).build();
+ std::vector<uint8_t> buffer = writeToBuffer<Font>(*original);
+
+ size_t baseHeapSize = getHeapSize();
+ {
+ BufferReader reader(buffer.data());
+ Font moveFrom(&reader);
+ Font moveTo(std::move(moveFrom));
+ EXPECT_EQ(nullptr, moveFrom.mExternalRefsHolder.load());
+ EXPECT_EQ(nullptr, moveTo.mExternalRefsHolder.load());
+ }
+ EXPECT_EQ(baseHeapSize, getHeapSize());
+ {
+ BufferReader reader(buffer.data());
+ Font moveFrom(&reader);
+ std::shared_ptr<MinikinFont> typeface = moveFrom.typeface();
+ Font moveTo(std::move(moveFrom));
+ EXPECT_EQ(nullptr, moveFrom.mExternalRefsHolder.load());
+ EXPECT_EQ(typeface, moveTo.typeface());
+ }
+ EXPECT_EQ(baseHeapSize, getHeapSize());
+}
+
+TEST(FontTest, MoveAssignmentTest) {
+ FreeTypeMinikinFontForTestFactory::init();
+ // Note: by definition, only BufferReader-based Font can be moved.
+ auto minikinFont = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath("Ascii.ttf"));
+ std::shared_ptr<Font> original = Font::Builder(minikinFont).build();
+ std::vector<uint8_t> buffer = writeToBuffer<Font>(*original);
+
+ size_t baseHeapSize = getHeapSize();
+ {
+ // mExternalRefsHolder: null -> null
+ BufferReader reader(buffer.data());
+ Font moveFrom(&reader);
+ BufferReader reader2(buffer.data());
+ Font moveTo(&reader2);
+ moveTo = std::move(moveFrom);
+ EXPECT_EQ(nullptr, moveFrom.mExternalRefsHolder.load());
+ EXPECT_EQ(nullptr, moveTo.mExternalRefsHolder.load());
+ }
+ EXPECT_EQ(baseHeapSize, getHeapSize());
+ {
+ // mExternalRefsHolder: non-null -> null
+ BufferReader reader(buffer.data());
+ Font moveFrom(&reader);
+ std::shared_ptr<MinikinFont> typeface = moveFrom.typeface();
+ BufferReader reader2(buffer.data());
+ Font moveTo(&reader2);
+ moveTo = std::move(moveFrom);
+ EXPECT_EQ(nullptr, moveFrom.mExternalRefsHolder.load());
+ EXPECT_EQ(typeface, moveTo.typeface());
+ }
+ EXPECT_EQ(baseHeapSize, getHeapSize());
+ {
+ // mExternalRefsHolder: null -> non-null
+ BufferReader reader(buffer.data());
+ Font moveFrom(&reader);
+ BufferReader reader2(buffer.data());
+ Font moveTo(&reader2);
+ moveTo.typeface();
+ moveTo = std::move(moveFrom);
+ EXPECT_EQ(nullptr, moveFrom.mExternalRefsHolder.load());
+ EXPECT_EQ(nullptr, moveTo.mExternalRefsHolder.load());
+ }
+ EXPECT_EQ(baseHeapSize, getHeapSize());
+ {
+ // mExternalRefsHolder: non-null -> non-null
+ BufferReader reader(buffer.data());
+ Font moveFrom(&reader);
+ std::shared_ptr<MinikinFont> typeface = moveFrom.typeface();
+ BufferReader reader2(buffer.data());
+ Font moveTo(&reader2);
+ moveTo.typeface();
+ moveTo = std::move(moveFrom);
+ EXPECT_EQ(nullptr, moveFrom.mExternalRefsHolder.load());
+ EXPECT_EQ(typeface, moveTo.typeface());
+ }
+ EXPECT_EQ(baseHeapSize, getHeapSize());
+}
+
} // namespace minikin
diff --git a/tests/unittest/GreedyLineBreakerTest.cpp b/tests/unittest/GreedyLineBreakerTest.cpp
index 3c395f4..e51ff83 100644
--- a/tests/unittest/GreedyLineBreakerTest.cpp
+++ b/tests/unittest/GreedyLineBreakerTest.cpp
@@ -75,7 +75,7 @@ protected:
auto family1 = buildFontFamily("Ascii.ttf");
auto family2 = buildFontFamily("CustomExtent.ttf");
std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
- auto fc = std::make_shared<FontCollection>(families);
+ auto fc = FontCollection::create(families);
MinikinPaint paint(fc);
paint.size = 10.0f; // Make 1em=10px
paint.localeListId = LocaleListCache::getId(lang);
@@ -98,7 +98,7 @@ TEST_F(GreedyLineBreakerTest, roundingError) {
MeasuredTextBuilder builder;
auto family1 = buildFontFamily("Ascii.ttf");
std::vector<std::shared_ptr<FontFamily>> families = {family1};
- auto fc = std::make_shared<FontCollection>(families);
+ auto fc = FontCollection::create(families);
MinikinPaint paint(fc);
paint.size = 56.0f; // Make 1em=56px
paint.scaleX = 1;
diff --git a/tests/unittest/LayoutCoreTest.cpp b/tests/unittest/LayoutCoreTest.cpp
index 2ab7543..4edc556 100644
--- a/tests/unittest/LayoutCoreTest.cpp
+++ b/tests/unittest/LayoutCoreTest.cpp
@@ -33,30 +33,29 @@ static LayoutPiece buildLayout(const std::string& text, const MinikinPaint& pain
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT);
}
-static LayoutPiece buildLayout(const std::string& text, const std::vector<std::string>& fonts) {
- std::vector<std::shared_ptr<FontFamily>> families;
- for (const auto& fontPath : fonts) {
- families.push_back(buildFontFamily(fontPath));
- }
- auto fc = std::make_shared<FontCollection>(families);
+static LayoutPiece buildLayout(const std::string& text, std::shared_ptr<FontCollection> fc) {
MinikinPaint paint(fc);
paint.size = 10.0f; // make 1em = 10px
return buildLayout(text, paint);
}
-static LayoutPiece buildLayout(const std::string& text, const std::vector<std::string>& fonts,
+static LayoutPiece buildLayout(const std::string& text, std::shared_ptr<FontCollection> fc,
const std::string fontFeaturesSettings) {
- std::vector<std::shared_ptr<FontFamily>> families;
- for (const auto& fontPath : fonts) {
- families.push_back(buildFontFamily(fontPath));
- }
- auto fc = std::make_shared<FontCollection>(families);
MinikinPaint paint(fc);
paint.size = 10.0f; // make 1em = 10px
paint.fontFeatureSettings = fontFeaturesSettings;
return buildLayout(text, paint);
}
+static std::shared_ptr<FontCollection> makeFontCollection(
+ std::initializer_list<std::string> fonts) {
+ std::vector<std::shared_ptr<FontFamily>> families;
+ for (const auto& fontPath : fonts) {
+ families.push_back(buildFontFamily(fontPath));
+ }
+ return FontCollection::create(families);
+}
+
TEST(LayoutPieceTest, doLayoutTest) {
// The LayoutTestFont.ttf has following coverage, extent, width and bbox.
// Ascender: 10em, Descender: -2em
@@ -71,7 +70,8 @@ TEST(LayoutPieceTest, doLayoutTest) {
// U+FFFD (invalid surrogate will be replaced to this): 7em, (0, 0) - (7, 7)
// U+10331 (\uD800\uDF31): 10em, (0, 0) - (10, 10)
{
- auto layout = buildLayout("I", {"LayoutTestFont.ttf"});
+ auto fc = makeFontCollection({"LayoutTestFont.ttf"});
+ auto layout = buildLayout("I", fc);
EXPECT_EQ(1u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(MinikinExtent(-100.0f, 20.0f), layout.extent());
@@ -82,7 +82,8 @@ TEST(LayoutPieceTest, doLayoutTest) {
EXPECT_EQ(10.0f, layout.advance());
}
{
- auto layout = buildLayout("II", {"LayoutTestFont.ttf"});
+ auto fc = makeFontCollection({"LayoutTestFont.ttf"});
+ auto layout = buildLayout("II", fc);
EXPECT_EQ(2u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
@@ -97,7 +98,8 @@ TEST(LayoutPieceTest, doLayoutTest) {
EXPECT_EQ(20.0f, layout.advance());
}
{
- auto layout = buildLayout("IV", {"LayoutTestFont.ttf"});
+ auto fc = makeFontCollection({"LayoutTestFont.ttf"});
+ auto layout = buildLayout("IV", fc);
EXPECT_EQ(2u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
@@ -123,7 +125,8 @@ TEST(LayoutPieceTest, doLayoutTest_MultiFont) {
// U+3048: 2em, (0, 0) - (2, 2)
// U+304A: 2em, (0, 0) - (2, 2)
{
- auto layout = buildLayout("I\u3042", {"LayoutTestFont.ttf", "Hiragana.ttf"});
+ auto fc = makeFontCollection({"LayoutTestFont.ttf", "Hiragana.ttf"});
+ auto layout = buildLayout("I\u3042", fc);
EXPECT_EQ(2u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(Point(10.0f, 0), layout.pointAt(1));
@@ -138,7 +141,8 @@ TEST(LayoutPieceTest, doLayoutTest_MultiFont) {
EXPECT_EQ(30.0f, layout.advance());
}
{
- auto layout = buildLayout("\u3042I", {"LayoutTestFont.ttf", "Hiragana.ttf"});
+ auto fc = makeFontCollection({"LayoutTestFont.ttf", "Hiragana.ttf"});
+ auto layout = buildLayout("\u3042I", fc);
EXPECT_EQ(2u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(Point(20.0f, 0), layout.pointAt(1));
@@ -160,7 +164,8 @@ TEST(LayoutPieceTest, doLayoutTest_Ligature) {
// U+0020..U+007E: 1em, (0, 0) - (1, 1)
// Also this has ligature entry for fi as "ccmp" feature, ff as "liga" feature.
{
- auto layout = buildLayout("fi", {"Ligature.ttf"});
+ auto fc = makeFontCollection({"Ligature.ttf"});
+ auto layout = buildLayout("fi", fc);
EXPECT_EQ(1u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
@@ -172,7 +177,8 @@ TEST(LayoutPieceTest, doLayoutTest_Ligature) {
EXPECT_EQ(10.0f, layout.advance());
}
{
- auto layout = buildLayout("ff", {"Ligature.ttf"});
+ auto fc = makeFontCollection({"Ligature.ttf"});
+ auto layout = buildLayout("ff", fc);
EXPECT_EQ(1u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
@@ -184,7 +190,8 @@ TEST(LayoutPieceTest, doLayoutTest_Ligature) {
EXPECT_EQ(10.0f, layout.advance());
}
{
- auto layout = buildLayout("fi", {"Ligature.ttf"}, "'liga' off");
+ auto fc = makeFontCollection({"Ligature.ttf"});
+ auto layout = buildLayout("fi", fc, "'liga' off");
EXPECT_EQ(1u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
@@ -196,7 +203,8 @@ TEST(LayoutPieceTest, doLayoutTest_Ligature) {
EXPECT_EQ(10.0f, layout.advance());
}
{
- auto layout = buildLayout("ff", {"Ligature.ttf"}, "'liga' off");
+ auto fc = makeFontCollection({"Ligature.ttf"});
+ auto layout = buildLayout("ff", fc, "'liga' off");
EXPECT_EQ(2u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
@@ -210,7 +218,8 @@ TEST(LayoutPieceTest, doLayoutTest_Ligature) {
EXPECT_EQ(20.0f, layout.advance());
}
{
- auto layout = buildLayout("fii", {"Ligature.ttf"});
+ auto fc = makeFontCollection({"Ligature.ttf"});
+ auto layout = buildLayout("fii", fc);
EXPECT_EQ(2u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
@@ -225,7 +234,8 @@ TEST(LayoutPieceTest, doLayoutTest_Ligature) {
EXPECT_EQ(20.0f, layout.advance());
}
{
- auto layout = buildLayout("if", {"Ligature.ttf"});
+ auto fc = makeFontCollection({"Ligature.ttf"});
+ auto layout = buildLayout("if", fc);
EXPECT_EQ(2u, layout.glyphCount());
EXPECT_EQ(Point(0, 0), layout.pointAt(0));
EXPECT_EQ(MinikinExtent(-80.0f, 20.0f), layout.extent());
diff --git a/tests/unittest/MeasurementTests.cpp b/tests/unittest/MeasurementTests.cpp
index b5a85c7..ff36f67 100644
--- a/tests/unittest/MeasurementTests.cpp
+++ b/tests/unittest/MeasurementTests.cpp
@@ -31,6 +31,15 @@ float getAdvance(const float* advances, const char* src) {
return getRunAdvance(advances, buf, 0, size, offset);
}
+void distributeAdvances(float* advances, const char* src, int count) {
+ const size_t BUF_SIZE = 256;
+ uint16_t buf[BUF_SIZE];
+ size_t offset;
+ size_t size;
+ ParseUnicode(buf, BUF_SIZE, src, &size, &offset);
+ distributeAdvances(advances, buf, offset, count);
+}
+
// Latin fi
TEST(Measurement, getRunAdvance_fi) {
const float unligated[] = {30.0, 20.0};
@@ -44,6 +53,22 @@ TEST(Measurement, getRunAdvance_fi) {
EXPECT_EQ(40.0, getAdvance(ligated, "'f' 'i' |"));
}
+TEST(Measurement, getRunAdvance_control_characters) {
+ const float unligated[] = {30.0, 20.0, 0.0, 0.0};
+ EXPECT_EQ(0.0, getAdvance(unligated, "| 'f' 'i' U+2066 U+202C"));
+ EXPECT_EQ(30.0, getAdvance(unligated, "'f' | 'i' U+2066 U+202C"));
+ EXPECT_EQ(50.0, getAdvance(unligated, "'f' 'i' | U+2066 U+202C"));
+ EXPECT_EQ(50.0, getAdvance(unligated, "'f' 'i' U+2066 | U+202C"));
+ EXPECT_EQ(50.0, getAdvance(unligated, "'f' 'i' U+2066 U+202C |"));
+
+ const float liagated[] = {40.0, 0.0, 0.0, 0.0};
+ EXPECT_EQ(0.0, getAdvance(liagated, "| 'f' 'i' U+2066 U+202C"));
+ EXPECT_EQ(20.0, getAdvance(liagated, "'f' | 'i' U+2066 U+202C"));
+ EXPECT_EQ(40.0, getAdvance(liagated, "'f' 'i' | U+2066 U+202C"));
+ EXPECT_EQ(40.0, getAdvance(liagated, "'f' 'i' U+2066 | U+202C"));
+ EXPECT_EQ(40.0, getAdvance(liagated, "'f' 'i' U+2066 U+202C |"));
+}
+
// Devanagari ka+virama+ka
TEST(Measurement, getRunAdvance_kka) {
const float unligated[] = {30.0, 0.0, 30.0};
@@ -59,4 +84,69 @@ TEST(Measurement, getRunAdvance_kka) {
EXPECT_EQ(30.0, getAdvance(ligated, "U+0915 U+094D U+0915 |"));
}
+TEST(Measurement, distributeAdvances_fi) {
+ float ligated[] = {20.0, 0.0};
+ distributeAdvances(ligated, "| 'f' 'i' ", 2);
+ EXPECT_EQ(ligated[0], 10.0);
+ EXPECT_EQ(ligated[1], 10.0);
+}
+
+TEST(Measurement, distributeAdvances_non_zero_start) {
+ // Note that advance[i] corresponding to (i + start)-th character.
+ float ligated[] = {20.0, 0.0};
+ distributeAdvances(ligated, "'a' 'b' | 'f' 'i' ", 2);
+ EXPECT_EQ(ligated[0], 10.0);
+ EXPECT_EQ(ligated[1], 10.0);
+}
+
+TEST(Measurement, distributeAdvances_non_zero_start_with_control_characters) {
+ // Note that advance[i] corresponding to (i + start)-th character.
+ float ligated[] = {20.0, 0.0, 0.0, 0.0};
+ distributeAdvances(ligated, "'a' U+2066 | 'f' 'i' U+2066 U+202C", 4);
+ EXPECT_EQ(ligated[0], 10.0);
+ EXPECT_EQ(ligated[1], 10.0);
+ EXPECT_EQ(ligated[2], 0.0);
+ EXPECT_EQ(ligated[3], 0.0);
+}
+
+TEST(Measurement, distributeAdvances_with_count) {
+ // Note that advance[i] corresponding to (i + start)-th character.
+ float ligated[] = {20.0, 0.0, 30.0, 0.0};
+ distributeAdvances(ligated, "'a' 'b' | 'f' 'i' 'f' 'i' ", 2);
+ EXPECT_EQ(ligated[0], 10.0);
+ EXPECT_EQ(ligated[1], 10.0);
+ // Count is 2, so it won't change the rest of the array.
+ EXPECT_EQ(ligated[2], 30.0);
+ EXPECT_EQ(ligated[3], 0.0);
+}
+
+TEST(Measurement, distributeAdvances_control_characters) {
+ float ligated[] = {20.0, 0.0, 0.0, 0.0};
+ distributeAdvances(ligated, "| 'f' 'i' U+2066 U+202C", 4);
+ EXPECT_EQ(ligated[0], 10.0);
+ EXPECT_EQ(ligated[1], 10.0);
+ EXPECT_EQ(ligated[2], 0.0);
+ EXPECT_EQ(ligated[3], 0.0);
+}
+
+TEST(Measurement, distributeAdvances_surrogate) {
+ float advances[] = {20.0, 0.0, 0.0, 0.0};
+ distributeAdvances(advances, "| U+D83D U+DE00 U+2066 U+202C", 4);
+ EXPECT_EQ(advances[0], 20.0);
+ EXPECT_EQ(advances[1], 0.0);
+ EXPECT_EQ(advances[2], 0.0);
+ EXPECT_EQ(advances[3], 0.0);
+}
+
+TEST(Measurement, distributeAdvances_surrogate_in_ligature) {
+ // If a ligature contains surrogates, advances is assigned to the first
+ // character in surrogate.
+ float ligated[] = {40.0, 0.0, 0.0, 0.0};
+ distributeAdvances(ligated, "| U+D83D U+DE00 U+D83D U+DE01", 4);
+ EXPECT_EQ(ligated[0], 20.0);
+ EXPECT_EQ(ligated[1], 0.0);
+ EXPECT_EQ(ligated[2], 20.0);
+ EXPECT_EQ(ligated[3], 0.0);
+}
+
} // namespace minikin
diff --git a/tests/unittest/OptimalLineBreakerTest.cpp b/tests/unittest/OptimalLineBreakerTest.cpp
index a5f8625..18619e3 100644
--- a/tests/unittest/OptimalLineBreakerTest.cpp
+++ b/tests/unittest/OptimalLineBreakerTest.cpp
@@ -71,7 +71,7 @@ protected:
auto family1 = buildFontFamily("Ascii.ttf");
auto family2 = buildFontFamily("CustomExtent.ttf");
std::vector<std::shared_ptr<FontFamily>> families = {family1, family2};
- auto fc = std::make_shared<FontCollection>(families);
+ auto fc = FontCollection::create(families);
MinikinPaint paint(fc);
paint.size = 10.0f; // Make 1em=10px
paint.localeListId = LocaleListCache::getId(lang);
@@ -1847,7 +1847,7 @@ TEST_F(OptimalLineBreakerTest, roundingError) {
MeasuredTextBuilder builder;
auto family1 = buildFontFamily("Ascii.ttf");
std::vector<std::shared_ptr<FontFamily>> families = {family1};
- auto fc = std::make_shared<FontCollection>(families);
+ auto fc = FontCollection::create(families);
MinikinPaint paint(fc);
paint.size = 56.0f; // Make 1em=56px
paint.scaleX = 1;
diff --git a/tests/unittest/SparseBitSetTest.cpp b/tests/unittest/SparseBitSetTest.cpp
index 8c67964..d79ebac 100644
--- a/tests/unittest/SparseBitSetTest.cpp
+++ b/tests/unittest/SparseBitSetTest.cpp
@@ -79,4 +79,8 @@ TEST(SparseBitSetTest, emptyBitSetBufferTest) {
ASSERT_EQ(buffer, newBuffer);
}
+TEST(SparseBitSetTest, sizeTest) {
+ ASSERT_EQ(sizeof(void*), sizeof(SparseBitSet));
+}
+
} // namespace minikin
diff --git a/tests/unittest/SystemFontsTest.cpp b/tests/unittest/SystemFontsTest.cpp
index f1b0109..1bff31f 100644
--- a/tests/unittest/SystemFontsTest.cpp
+++ b/tests/unittest/SystemFontsTest.cpp
@@ -95,8 +95,8 @@ TEST(SystemFontTest, getAvailableFont_dedupFonts) {
auto fc1Families = std::vector<std::shared_ptr<FontFamily>>{asciiFamily, boldItalicFamily};
auto fc2Families = std::vector<std::shared_ptr<FontFamily>>{boldFamily, boldItalicFamily};
- auto fc1 = std::make_shared<FontCollection>(std::move(fc1Families));
- auto fc2 = std::make_shared<FontCollection>(std::move(fc2Families));
+ auto fc1 = FontCollection::create(std::move(fc1Families));
+ auto fc2 = FontCollection::create(std::move(fc2Families));
systemFonts.addFontMap(std::move(fc1));
systemFonts.addFontMap(std::move(fc2));
diff --git a/tests/util/BufferUtils.h b/tests/util/BufferUtils.h
index 355e74e..015a939 100644
--- a/tests/util/BufferUtils.h
+++ b/tests/util/BufferUtils.h
@@ -46,14 +46,6 @@ std::vector<uint8_t> writeToBuffer(const T& t) {
return buffer;
}
-template <class T, auto arg>
-std::vector<uint8_t> writeToBuffer(const T& t) {
- std::vector<uint8_t> buffer = allocateBuffer<T, arg>(t);
- BufferWriter writer(buffer.data());
- t.template writeTo<arg>(&writer);
- return buffer;
-}
-
} // namespace minikin
#endif // MINIKIN_TEST_BUFFER_UTILS_H
diff --git a/tests/util/FontTestUtils.cpp b/tests/util/FontTestUtils.cpp
index 5370ab6..a1025fe 100644
--- a/tests/util/FontTestUtils.cpp
+++ b/tests/util/FontTestUtils.cpp
@@ -106,11 +106,12 @@ std::vector<std::shared_ptr<FontFamily>> getFontFamilies(const std::string& font
xmlChar* lang = xmlGetProp(familyNode, (const xmlChar*)"lang");
std::shared_ptr<FontFamily> family;
if (lang == nullptr) {
- family = std::make_shared<FontFamily>(variant, std::move(fonts));
+ family = FontFamily::create(variant, std::move(fonts));
} else {
uint32_t langId = registerLocaleList(std::string((const char*)lang, xmlStrlen(lang)));
- family = std::make_shared<FontFamily>(langId, variant, std::move(fonts),
- false /* isCustomFallback */);
+ family =
+ FontFamily::create(langId, variant, std::move(fonts),
+ false /* isCustomFallback */, false /* isdefaultFallback */);
}
families.push_back(family);
}
@@ -119,14 +120,14 @@ std::vector<std::shared_ptr<FontFamily>> getFontFamilies(const std::string& font
}
std::shared_ptr<FontCollection> buildFontCollection(const std::string& filePath) {
- return std::make_shared<FontCollection>(buildFontFamily(filePath));
+ return FontCollection::create(buildFontFamily(filePath));
}
std::shared_ptr<FontFamily> buildFontFamily(const std::string& filePath) {
auto font = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(filePath));
std::vector<std::shared_ptr<Font>> fonts;
fonts.push_back(Font::Builder(font).build());
- return std::make_shared<FontFamily>(std::move(fonts));
+ return FontFamily::create(std::move(fonts));
}
std::shared_ptr<FontFamily> buildFontFamily(const std::string& filePath, const std::string& lang,
@@ -134,8 +135,8 @@ std::shared_ptr<FontFamily> buildFontFamily(const std::string& filePath, const s
auto font = std::make_shared<FreeTypeMinikinFontForTest>(getTestFontPath(filePath));
std::vector<std::shared_ptr<Font>> fonts;
fonts.push_back(Font::Builder(font).build());
- return std::make_shared<FontFamily>(LocaleListCache::getId(lang), FamilyVariant::DEFAULT,
- std::move(fonts), isCustomFallback);
+ return FontFamily::create(LocaleListCache::getId(lang), FamilyVariant::DEFAULT,
+ std::move(fonts), isCustomFallback, false /* isDefaultFallback */);
}
} // namespace minikin
diff --git a/tests/util/FontTestUtils.h b/tests/util/FontTestUtils.h
index 660438b..63748e4 100644
--- a/tests/util/FontTestUtils.h
+++ b/tests/util/FontTestUtils.h
@@ -19,9 +19,8 @@
#include <memory>
-#include "minikin/FontCollection.h"
-
#include "PathUtils.h"
+#include "minikin/FontCollection.h"
namespace minikin {
@@ -40,8 +39,7 @@ std::vector<std::shared_ptr<FontFamily>> getFontFamilies(const std::string& font
* The XML path and font files are needed to be in the test data directory.
*/
inline std::shared_ptr<FontCollection> buildFontCollectionFromXml(const std::string& xmlPath) {
- return std::make_shared<FontCollection>(
- getFontFamilies(getTestDataDir(), getTestDataDir() + xmlPath));
+ return FontCollection::create(getFontFamilies(getTestDataDir(), getTestDataDir() + xmlPath));
}
/**
diff --git a/tests/util/FreeTypeMinikinFontForTest.cpp b/tests/util/FreeTypeMinikinFontForTest.cpp
index 1be466a..ce36ec0 100644
--- a/tests/util/FreeTypeMinikinFontForTest.cpp
+++ b/tests/util/FreeTypeMinikinFontForTest.cpp
@@ -112,18 +112,27 @@ void FreeTypeMinikinFontForTest::GetFontExtent(MinikinExtent* extent, const Mini
extent->descent = -static_cast<float>(mFtFace->descender) * paint.size / upem;
}
-void writeFreeTypeMinikinFontForTest(BufferWriter* writer, const MinikinFont* typeface) {
+FreeTypeMinikinFontForTestFactory::FreeTypeMinikinFontForTestFactory() : MinikinFontFactory() {
+ MinikinFontFactory::setInstance(this);
+}
+
+// static
+void FreeTypeMinikinFontForTestFactory::init() {
+ static FreeTypeMinikinFontForTestFactory factory;
+}
+
+void FreeTypeMinikinFontForTestFactory::write(BufferWriter* writer,
+ const MinikinFont* typeface) const {
writer->writeString(typeface->GetFontPath());
}
-std::shared_ptr<MinikinFont> loadFreeTypeMinikinFontForTest(BufferReader reader) {
+std::shared_ptr<MinikinFont> FreeTypeMinikinFontForTestFactory::create(BufferReader reader) const {
std::string fontPath(reader.readString());
return std::make_shared<FreeTypeMinikinFontForTest>(fontPath);
}
-Font::TypefaceLoader* readFreeTypeMinikinFontForTest(BufferReader* reader) {
+void FreeTypeMinikinFontForTestFactory::skip(BufferReader* reader) const {
reader->skipString(); // fontPath
- return &loadFreeTypeMinikinFontForTest;
}
} // namespace minikin
diff --git a/tests/util/FreeTypeMinikinFontForTest.h b/tests/util/FreeTypeMinikinFontForTest.h
index 4cdb6d8..6903deb 100644
--- a/tests/util/FreeTypeMinikinFontForTest.h
+++ b/tests/util/FreeTypeMinikinFontForTest.h
@@ -17,13 +17,14 @@
#ifndef MINIKIN_TEST_FREE_TYPE_MINIKIN_FONT_FOR_TEST_H
#define MINIKIN_TEST_FREE_TYPE_MINIKIN_FONT_FOR_TEST_H
+#include <ft2build.h>
+
#include <string>
#include "minikin/Buffer.h"
#include "minikin/Font.h"
#include "minikin/MinikinFont.h"
-
-#include <ft2build.h>
+#include "minikin/MinikinFontFactory.h"
#include FT_FREETYPE_H
#include "minikin/Macros.h"
@@ -64,9 +65,19 @@ private:
MINIKIN_PREVENT_COPY_AND_ASSIGN(FreeTypeMinikinFontForTest);
};
-void writeFreeTypeMinikinFontForTest(BufferWriter* writer, const MinikinFont* typeface);
+class FreeTypeMinikinFontForTestFactory : MinikinFontFactory {
+private:
+ FreeTypeMinikinFontForTestFactory();
+
+public:
+ static void init();
+
+ void write(BufferWriter* writer, const MinikinFont* typeface) const override;
-Font::TypefaceLoader* readFreeTypeMinikinFontForTest(BufferReader* reader);
+ std::shared_ptr<MinikinFont> create(BufferReader reader) const override;
+
+ void skip(BufferReader* reader) const override;
+};
} // namespace minikin