OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/android/vr_shell/font_fallback.h" |
| 6 |
| 7 #include <map> |
| 8 #include <set> |
| 9 #include <string> |
| 10 #include <vector> |
| 11 |
| 12 #include "base/lazy_instance.h" |
| 13 #include "base/memory/ptr_util.h" |
| 14 #include "third_party/icu/source/common/unicode/uscript.h" |
| 15 #include "third_party/skia/include/core/SkPaint.h" |
| 16 #include "third_party/skia/include/core/SkTypeface.h" |
| 17 #include "third_party/skia/include/ports/SkFontMgr.h" |
| 18 #include "ui/gfx/platform_font_linux.h" |
| 19 |
| 20 namespace vr_shell { |
| 21 |
| 22 namespace { |
| 23 |
| 24 enum KnownGlyph { |
| 25 UNDEFINED, |
| 26 UNKNOWN, |
| 27 KNOWN, |
| 28 }; |
| 29 |
| 30 class CachedFont { |
| 31 public: |
| 32 static std::unique_ptr<CachedFont> CreateForTypeface( |
| 33 sk_sp<SkTypeface> typeface) { |
| 34 return base::WrapUnique<CachedFont>(new CachedFont(std::move(typeface))); |
| 35 } |
| 36 |
| 37 bool HasGlyphForCharacter(UChar32 character) { |
| 38 // In order to increase cache hits, the cache is script based rather than |
| 39 // character based. This also limits the cache size to the number of Unicode |
| 40 // scripts (174 at the time of writing). |
| 41 UErrorCode err = UErrorCode::U_ZERO_ERROR; |
| 42 UScriptCode script = uscript_getScript(character, &err); |
| 43 if (!U_SUCCESS(err) || script == UScriptCode::USCRIPT_INVALID_CODE) |
| 44 return false; |
| 45 auto& supported = supported_scripts_[script]; |
| 46 if (supported != UNDEFINED) |
| 47 return supported == KNOWN; |
| 48 uint16_t glyph_id; |
| 49 paint_.textToGlyphs(&character, sizeof(UChar32), &glyph_id); |
| 50 supported = glyph_id ? KNOWN : UNKNOWN; |
| 51 return glyph_id; |
| 52 } |
| 53 std::string GetFontName() { return name_; } |
| 54 |
| 55 private: |
| 56 explicit CachedFont(sk_sp<SkTypeface> skia_face) { |
| 57 SkString sk_name; |
| 58 skia_face->getFamilyName(&sk_name); |
| 59 name_ = std::string(sk_name.c_str(), sk_name.size()); |
| 60 paint_.setTypeface(std::move(skia_face)); |
| 61 paint_.setTextEncoding(SkPaint::kUTF32_TextEncoding); |
| 62 } |
| 63 |
| 64 SkPaint paint_; |
| 65 std::map<UScriptCode, KnownGlyph> supported_scripts_; |
| 66 std::string name_; |
| 67 |
| 68 DISALLOW_COPY_AND_ASSIGN(CachedFont); |
| 69 }; |
| 70 |
| 71 using FontCache = std::map<SkFontID, std::unique_ptr<CachedFont>>; |
| 72 base::LazyInstance<FontCache>::Leaky g_fonts = LAZY_INSTANCE_INITIALIZER; |
| 73 |
| 74 class CachedFontSet { |
| 75 public: |
| 76 CachedFontSet() : locale_() {} |
| 77 ~CachedFontSet() = default; |
| 78 |
| 79 void SetLocale(const std::string& locale) { |
| 80 // Store font list for one locale at a time. |
| 81 if (locale != locale_) { |
| 82 font_ids_.clear(); |
| 83 unknown_chars_.clear(); |
| 84 locale_ = locale; |
| 85 } |
| 86 } |
| 87 |
| 88 std::string GetFallbackFontNameForChar(UChar32 c) { |
| 89 if (unknown_chars_.find(c) != unknown_chars_.end()) |
| 90 return ""; |
| 91 for (SkFontID font_id : font_ids_) { |
| 92 std::unique_ptr<CachedFont>& font = g_fonts.Get()[font_id]; |
| 93 if (font->HasGlyphForCharacter(c)) |
| 94 return font->GetFontName(); |
| 95 } |
| 96 sk_sp<SkFontMgr> font_mgr(SkFontMgr::RefDefault()); |
| 97 const char* bcp47_locales[] = {locale_.c_str()}; |
| 98 sk_sp<SkTypeface> tf(font_mgr->matchFamilyStyleCharacter( |
| 99 nullptr, SkFontStyle(), locale_.empty() ? nullptr : bcp47_locales, |
| 100 locale_.empty() ? 0 : 1, c)); |
| 101 if (tf) { |
| 102 SkFontID font_id = tf->uniqueID(); |
| 103 font_ids_.push_back(font_id); |
| 104 std::unique_ptr<CachedFont>& cached_font = g_fonts.Get()[font_id]; |
| 105 if (!cached_font) |
| 106 cached_font = CachedFont::CreateForTypeface(tf); |
| 107 return cached_font->GetFontName(); |
| 108 } |
| 109 unknown_chars_.insert(c); |
| 110 return std::string(); |
| 111 } |
| 112 |
| 113 private: |
| 114 std::string locale_; |
| 115 std::vector<SkFontID> font_ids_; |
| 116 std::set<UChar32> unknown_chars_; |
| 117 |
| 118 DISALLOW_COPY_AND_ASSIGN(CachedFontSet); |
| 119 }; |
| 120 |
| 121 base::LazyInstance<CachedFontSet>::Leaky g_cached_font_set = |
| 122 LAZY_INSTANCE_INITIALIZER; |
| 123 |
| 124 bool FontSupportsChar(const gfx::Font& font, UChar32 c) { |
| 125 sk_sp<SkTypeface> typeface = |
| 126 static_cast<gfx::PlatformFontLinux*>(font.platform_font())->typeface(); |
| 127 std::unique_ptr<CachedFont>& cached_font = |
| 128 g_fonts.Get()[typeface->uniqueID()]; |
| 129 if (!cached_font) |
| 130 cached_font = CachedFont::CreateForTypeface(std::move(typeface)); |
| 131 return cached_font->HasGlyphForCharacter(c); |
| 132 } |
| 133 |
| 134 } // namespace |
| 135 |
| 136 std::string GetFallbackFontNameForChar(const gfx::Font& default_font, |
| 137 UChar32 c, |
| 138 const std::string& locale) { |
| 139 if (FontSupportsChar(default_font, c)) |
| 140 return std::string(); |
| 141 CachedFontSet& cached_font_set = g_cached_font_set.Get(); |
| 142 cached_font_set.SetLocale(locale); |
| 143 return cached_font_set.GetFallbackFontNameForChar(c); |
| 144 } |
| 145 |
| 146 } // namespace vr_shell |
OLD | NEW |