mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
libtxt: extend Minikin to find a fallback font if its font collection can not match a given character (#4623)
Minikin layout uses a FontCollection containing a list of pre-selected fonts for a particular font family. This patch extends the FontCollection to invoke a hook provided by libtxt if layout sees a character that can not be rendered by any font in the collection. * Change the Minikin lock to a recursive mutex. This is required because the fallback font provider may create new fonts during a layout operation that already holds the lock. * Implement a fallback font provider hook that queries Skia for fonts matching an unrecognized character. * Maintain a cache of fallback fonts. Prepopulate the cache with fonts covering some commonly used character classes. * Add a last resort font list for cases where Skia's font manager can not find any font for a character (similar to Blink's FontCache::getLastResortFallbackFont)
This commit is contained in:
parent
15430edc1f
commit
de34a3e272
13
third_party/txt/src/minikin/FontCollection.cpp
vendored
13
third_party/txt/src/minikin/FontCollection.cpp
vendored
@ -59,7 +59,7 @@ FontCollection::FontCollection(
|
||||
|
||||
void FontCollection::init(
|
||||
const vector<std::shared_ptr<FontFamily>>& typefaces) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
mId = sNextId++;
|
||||
vector<uint32_t> lastChar;
|
||||
size_t nTypefaces = typefaces.size();
|
||||
@ -321,6 +321,15 @@ const std::shared_ptr<FontFamily>& FontCollection::getFamilyForChar(
|
||||
}
|
||||
}
|
||||
if (bestFamilyIndex == -1) {
|
||||
// libtxt: check if the fallback font provider can match this character
|
||||
if (mFallbackFontProvider) {
|
||||
const std::shared_ptr<FontFamily>& fallback =
|
||||
mFallbackFontProvider->matchFallbackFont(ch);
|
||||
if (fallback) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode);
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
@ -380,7 +389,7 @@ bool FontCollection::hasVariationSelector(uint32_t baseCodepoint,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
|
||||
// Currently mRanges can not be used here since it isn't aware of the
|
||||
// variation sequence.
|
||||
|
||||
16
third_party/txt/src/minikin/FontCollection.h
vendored
16
third_party/txt/src/minikin/FontCollection.h
vendored
@ -32,6 +32,15 @@ class FontCollection {
|
||||
const std::vector<std::shared_ptr<FontFamily>>& typefaces);
|
||||
explicit FontCollection(std::shared_ptr<FontFamily>&& typeface);
|
||||
|
||||
// libtxt extension: an interface for looking up fallback fonts for characters
|
||||
// that do not match this collection's font families.
|
||||
class FallbackFontProvider {
|
||||
public:
|
||||
virtual ~FallbackFontProvider() = default;
|
||||
virtual const std::shared_ptr<FontFamily>& matchFallbackFont(
|
||||
uint32_t ch) = 0;
|
||||
};
|
||||
|
||||
struct Run {
|
||||
FakedFont fakedFont;
|
||||
int start;
|
||||
@ -63,6 +72,10 @@ class FontCollection {
|
||||
|
||||
uint32_t getId() const;
|
||||
|
||||
void set_fallback_font_provider(std::unique_ptr<FallbackFontProvider> ffp) {
|
||||
mFallbackFontProvider = std::move(ffp);
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kLogCharsPerPage = 8;
|
||||
static const int kPageMask = (1 << kLogCharsPerPage) - 1;
|
||||
@ -131,6 +144,9 @@ class FontCollection {
|
||||
|
||||
// Set of supported axes in this collection.
|
||||
std::unordered_set<AxisTag> mSupportedAxes;
|
||||
|
||||
// libtxt extension: Fallback font provider.
|
||||
std::unique_ptr<FallbackFontProvider> mFallbackFontProvider;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
8
third_party/txt/src/minikin/FontFamily.cpp
vendored
8
third_party/txt/src/minikin/FontFamily.cpp
vendored
@ -56,7 +56,7 @@ android::hash_t FontStyle::hash() const {
|
||||
|
||||
// static
|
||||
uint32_t FontStyle::registerLanguageList(const std::string& languages) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
return FontLanguageListCache::getId(languages);
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
|
||||
bool FontFamily::analyzeStyle(const std::shared_ptr<MinikinFont>& typeface,
|
||||
int* weight,
|
||||
bool* italic) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
|
||||
HbBlob os2Table(getFontTable(typeface.get(), os2Tag));
|
||||
if (os2Table.get() == nullptr)
|
||||
@ -175,7 +175,7 @@ bool FontFamily::isColorEmojiFamily() const {
|
||||
}
|
||||
|
||||
void FontFamily::computeCoverage() {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
const FontStyle defaultStyle;
|
||||
const MinikinFont* typeface = getClosestMatch(defaultStyle).font;
|
||||
const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
|
||||
@ -233,7 +233,7 @@ std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
|
||||
std::vector<Font> fonts;
|
||||
for (const Font& font : mFonts) {
|
||||
bool supportedVariations = false;
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxesLocked();
|
||||
if (!supportedAxes.empty()) {
|
||||
for (const FontVariation& variation : variations) {
|
||||
|
||||
6
third_party/txt/src/minikin/Layout.cpp
vendored
6
third_party/txt/src/minikin/Layout.cpp
vendored
@ -604,7 +604,7 @@ void Layout::doLayout(const uint16_t* buf,
|
||||
const FontStyle& style,
|
||||
const MinikinPaint& paint,
|
||||
const std::shared_ptr<FontCollection>& collection) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
|
||||
LayoutContext ctx;
|
||||
ctx.style = style;
|
||||
@ -628,7 +628,7 @@ float Layout::measureText(const uint16_t* buf,
|
||||
const MinikinPaint& paint,
|
||||
const std::shared_ptr<FontCollection>& collection,
|
||||
float* advances) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
|
||||
LayoutContext ctx;
|
||||
ctx.style = style;
|
||||
@ -1197,7 +1197,7 @@ void Layout::getBounds(MinikinRect* bounds) const {
|
||||
}
|
||||
|
||||
void Layout::purgeCaches() {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache;
|
||||
layoutCache.clear();
|
||||
purgeHbFontCacheLocked();
|
||||
|
||||
2
third_party/txt/src/minikin/MinikinFont.cpp
vendored
2
third_party/txt/src/minikin/MinikinFont.cpp
vendored
@ -21,7 +21,7 @@
|
||||
namespace minikin {
|
||||
|
||||
MinikinFont::~MinikinFont() {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::lock_guard<std::recursive_mutex> _l(gMinikinLock);
|
||||
purgeHbFontLocked(this);
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
namespace minikin {
|
||||
|
||||
std::mutex gMinikinLock;
|
||||
std::recursive_mutex gMinikinLock;
|
||||
|
||||
void assertMinikinLocked() {
|
||||
#ifdef ENABLE_RACE_DETECTION
|
||||
|
||||
@ -31,7 +31,7 @@ namespace minikin {
|
||||
// Presently, that's implemented by through a global lock, and having
|
||||
// all external interfaces take that lock.
|
||||
|
||||
extern std::mutex gMinikinLock;
|
||||
extern std::recursive_mutex gMinikinLock;
|
||||
|
||||
// Aborts if gMinikinLock is not acquired. Do nothing on the release build.
|
||||
void assertMinikinLocked();
|
||||
|
||||
96
third_party/txt/src/txt/font_collection.cc
vendored
96
third_party/txt/src/txt/font_collection.cc
vendored
@ -41,8 +41,29 @@ const std::vector<SkUnichar> fallback_characters{
|
||||
0x627, // Arabic
|
||||
};
|
||||
|
||||
// Font families that will be used as a last resort if no font manager provides
|
||||
// a font matching a particular character.
|
||||
const std::vector<std::string> last_resort_fonts{
|
||||
"Arial",
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class TxtFallbackFontProvider
|
||||
: public minikin::FontCollection::FallbackFontProvider {
|
||||
public:
|
||||
TxtFallbackFontProvider(std::shared_ptr<FontCollection> font_collection)
|
||||
: font_collection_(font_collection) {}
|
||||
|
||||
virtual const std::shared_ptr<minikin::FontFamily>& matchFallbackFont(
|
||||
uint32_t ch) {
|
||||
return font_collection_->MatchFallbackFont(ch);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<FontCollection> font_collection_;
|
||||
};
|
||||
|
||||
FontCollection::FontCollection() = default;
|
||||
|
||||
FontCollection::~FontCollection() = default;
|
||||
@ -117,6 +138,8 @@ FontCollection::GetMinikinFontCollectionForFamily(const std::string& family) {
|
||||
// Create the minikin font collection.
|
||||
auto font_collection =
|
||||
std::make_shared<minikin::FontCollection>(std::move(minikin_families));
|
||||
font_collection->set_fallback_font_provider(
|
||||
std::make_unique<TxtFallbackFontProvider>(shared_from_this()));
|
||||
|
||||
// Cache the font collection for future queries.
|
||||
font_collections_cache_[family] = font_collection;
|
||||
@ -133,29 +156,62 @@ FontCollection::GetMinikinFontCollectionForFamily(const std::string& family) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::shared_ptr<minikin::FontFamily>& FontCollection::MatchFallbackFont(
|
||||
uint32_t ch) {
|
||||
for (const auto& manager : skia_font_managers_) {
|
||||
sk_sp<SkTypeface> typeface(
|
||||
manager->matchFamilyStyleCharacter(0, SkFontStyle(), nullptr, 0, ch));
|
||||
if (!typeface)
|
||||
continue;
|
||||
|
||||
return GetFontFamilyForTypeface(typeface);
|
||||
}
|
||||
|
||||
return null_family_;
|
||||
}
|
||||
|
||||
const std::shared_ptr<minikin::FontFamily>&
|
||||
FontCollection::GetFontFamilyForTypeface(const sk_sp<SkTypeface>& typeface) {
|
||||
SkFontID typeface_id = typeface->uniqueID();
|
||||
auto fallback_it = fallback_fonts_.find(typeface_id);
|
||||
if (fallback_it != fallback_fonts_.end()) {
|
||||
return fallback_it->second;
|
||||
}
|
||||
|
||||
std::vector<minikin::Font> minikin_fonts;
|
||||
minikin_fonts.emplace_back(std::make_shared<FontSkia>(typeface),
|
||||
minikin::FontStyle());
|
||||
auto insert_it = fallback_fonts_.insert(std::make_pair(
|
||||
typeface_id,
|
||||
std::make_shared<minikin::FontFamily>(std::move(minikin_fonts))));
|
||||
|
||||
// Clear the cache to force creation of new font collections that will include
|
||||
// this fallback font.
|
||||
font_collections_cache_.clear();
|
||||
|
||||
return insert_it.first->second;
|
||||
}
|
||||
|
||||
void FontCollection::UpdateFallbackFonts(sk_sp<SkFontMgr> manager) {
|
||||
char language_tag[ULOC_FULLNAME_CAPACITY];
|
||||
UErrorCode uerr;
|
||||
uloc_toLanguageTag(icu::Locale::getDefault().getName(), language_tag,
|
||||
ULOC_FULLNAME_CAPACITY, FALSE, &uerr);
|
||||
if (U_FAILURE(uerr))
|
||||
return;
|
||||
const char* bcp47[] = {language_tag};
|
||||
|
||||
// Prepopulate the fallback font cache with fonts matching some widely
|
||||
// used character classes.
|
||||
for (SkUnichar fallback_char : fallback_characters) {
|
||||
if (fallback_fonts_.count(fallback_char))
|
||||
continue;
|
||||
sk_sp<SkTypeface> typeface(manager->matchFamilyStyleCharacter(
|
||||
0, SkFontStyle(), nullptr, 0, fallback_char));
|
||||
if (typeface) {
|
||||
// Create a Minikin font family for this typeface if one does not already
|
||||
// exist.
|
||||
GetFontFamilyForTypeface(typeface);
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkTypeface> skia_typeface(manager->matchFamilyStyleCharacter(
|
||||
0, SkFontStyle(), bcp47, 1, fallback_char));
|
||||
if (!skia_typeface)
|
||||
continue;
|
||||
|
||||
std::vector<minikin::Font> minikin_fonts;
|
||||
minikin_fonts.emplace_back(std::make_shared<FontSkia>(skia_typeface),
|
||||
minikin::FontStyle());
|
||||
fallback_fonts_[fallback_char] =
|
||||
std::make_shared<minikin::FontFamily>(std::move(minikin_fonts));
|
||||
// Add additional font families to be used if nothing else matches.
|
||||
for (const std::string& family : last_resort_fonts) {
|
||||
sk_sp<SkTypeface> typeface(
|
||||
manager->matchFamilyStyle(family.c_str(), SkFontStyle()));
|
||||
if (typeface) {
|
||||
GetFontFamilyForTypeface(typeface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
third_party/txt/src/txt/font_collection.h
vendored
10
third_party/txt/src/txt/font_collection.h
vendored
@ -31,7 +31,7 @@
|
||||
|
||||
namespace txt {
|
||||
|
||||
class FontCollection {
|
||||
class FontCollection : public std::enable_shared_from_this<FontCollection> {
|
||||
public:
|
||||
FontCollection();
|
||||
|
||||
@ -46,12 +46,18 @@ class FontCollection {
|
||||
std::shared_ptr<minikin::FontCollection> GetMinikinFontCollectionForFamily(
|
||||
const std::string& family);
|
||||
|
||||
const std::shared_ptr<minikin::FontFamily>& MatchFallbackFont(uint32_t ch);
|
||||
|
||||
private:
|
||||
std::deque<sk_sp<SkFontMgr>> skia_font_managers_;
|
||||
std::unordered_map<std::string, std::shared_ptr<minikin::FontCollection>>
|
||||
font_collections_cache_;
|
||||
std::unordered_map<SkUnichar, std::shared_ptr<minikin::FontFamily>>
|
||||
std::unordered_map<SkFontID, std::shared_ptr<minikin::FontFamily>>
|
||||
fallback_fonts_;
|
||||
std::shared_ptr<minikin::FontFamily> null_family_;
|
||||
|
||||
const std::shared_ptr<minikin::FontFamily>& GetFontFamilyForTypeface(
|
||||
const sk_sp<SkTypeface>& typeface);
|
||||
|
||||
void UpdateFallbackFonts(sk_sp<SkFontMgr> manager);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user