Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Side by Side Diff: content/child/dwrite_font_proxy/font_fallback_win.cc

Issue 1906633003: Implement fallback cache (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@blinkfontfallback
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/child/dwrite_font_proxy/font_fallback_win.h" 5 #include "content/child/dwrite_font_proxy/font_fallback_win.h"
6 6
7 #include <algorithm>
8
9 #include "base/metrics/histogram_macros.h"
7 #include "base/strings/string16.h" 10 #include "base/strings/string16.h"
8 11 #include "base/strings/utf_string_conversion_utils.h"
9 #include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h" 12 #include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h"
10 #include "content/common/dwrite_font_proxy_messages.h" 13 #include "content/common/dwrite_font_proxy_messages.h"
11 #include "content/public/child/child_thread.h" 14 #include "content/public/child/child_thread.h"
12 #include "ipc/ipc_sender.h" 15 #include "ipc/ipc_sender.h"
13 16
14 namespace mswr = Microsoft::WRL; 17 namespace mswr = Microsoft::WRL;
15 18
16 namespace content { 19 namespace content {
17 20
21 namespace {
22
23 const size_t kMaxFamilyCacheSize = 10;
24
25 // This enum is used to define the buckets for an enumerated UMA histogram.
26 // Hence,
27 // (a) existing enumerated constants should never be deleted or reordered, and
28 // (b) new constants should only be appended at the end of the enumeration.
29 enum DirectWriteFontFallbackResult {
30 FAILED_NO_FONT = 0,
31 SUCCESS_CACHE = 1,
32 SUCCESS_IPC = 2,
33
34 FONT_FALLBACK_RESULT_MAX_VALUE
35 };
36
37 void LogFallbackResult(DirectWriteFontFallbackResult fallback_result) {
38 UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.FallbackResult",
39 fallback_result, FONT_FALLBACK_RESULT_MAX_VALUE);
40 }
41
42 } // namespace
43
18 FontFallback::FontFallback() = default; 44 FontFallback::FontFallback() = default;
19 FontFallback::~FontFallback() = default; 45 FontFallback::~FontFallback() = default;
20 46
21 HRESULT FontFallback::MapCharacters(IDWriteTextAnalysisSource* source, 47 HRESULT FontFallback::MapCharacters(IDWriteTextAnalysisSource* source,
22 UINT32 text_position, 48 UINT32 text_position,
23 UINT32 text_length, 49 UINT32 text_length,
24 IDWriteFontCollection* base_font_collection, 50 IDWriteFontCollection* base_font_collection,
25 const wchar_t* base_family_name, 51 const wchar_t* base_family_name,
26 DWRITE_FONT_WEIGHT base_weight, 52 DWRITE_FONT_WEIGHT base_weight,
27 DWRITE_FONT_STYLE base_style, 53 DWRITE_FONT_STYLE base_style,
28 DWRITE_FONT_STRETCH base_stretch, 54 DWRITE_FONT_STRETCH base_stretch,
29 UINT32* mapped_length, 55 UINT32* mapped_length,
30 IDWriteFont** mapped_font, 56 IDWriteFont** mapped_font,
31 FLOAT* scale) { 57 FLOAT* scale) {
32 *mapped_font = nullptr; 58 *mapped_font = nullptr;
33 *mapped_length = 1; 59 *mapped_length = 1;
34 *scale = 1.0; 60 *scale = 1.0;
35 61
36 const WCHAR* text = nullptr; 62 const WCHAR* text = nullptr;
37 UINT32 chunk_length = 0; 63 UINT32 chunk_length = 0;
38 if (FAILED(source->GetTextAtPosition(text_position, &text, &chunk_length))) { 64 if (FAILED(source->GetTextAtPosition(text_position, &text, &chunk_length))) {
39 DCHECK(false); 65 DCHECK(false);
40 return E_FAIL; 66 return E_FAIL;
41 } 67 }
42 base::string16 text_chunk(text, chunk_length); 68 base::string16 text_chunk(text, std::min(chunk_length, text_length));
69
70 base_family_name = base_family_name ? base_family_name : L"";
71
72 if (GetCachedFont(text_chunk, base_family_name, base_weight, base_style,
scottmg 2016/04/21 22:45:49 It looks like if text_chunk.size() == 0, this will
Ilya Kulshin 2016/04/22 00:48:37 That would mean we were asked to map an empty stri
73 base_stretch, mapped_font, mapped_length)) {
74 DCHECK(*mapped_font);
75 DCHECK_GT(*mapped_length, 0u);
scottmg 2016/04/21 22:45:49 You didn't cache or return *scale here. Are you ce
Ilya Kulshin 2016/04/22 00:48:37 I've never (and by never I mean "in the past month
76 LogFallbackResult(SUCCESS_CACHE);
77 return S_OK;
78 }
79
80 TRACE_EVENT0("dwrite", "FontFallback::MapCharacters (IPC)");
43 81
44 const WCHAR* locale = nullptr; 82 const WCHAR* locale = nullptr;
45 // |locale_text_length| is actually the length of text with the locale, not 83 // |locale_text_length| is actually the length of text with the locale, not
46 // the length of the locale string itself. 84 // the length of the locale string itself.
47 UINT32 locale_text_length = 0; 85 UINT32 locale_text_length = 0;
48 source->GetLocaleName(text_position /*textPosition*/, &locale_text_length, 86 source->GetLocaleName(text_position /*textPosition*/, &locale_text_length,
49 &locale); 87 &locale);
50 88
51 if (locale == nullptr) 89 locale = locale ? locale : L"";
52 locale = L"";
53 90
54 DWriteFontStyle style; 91 DWriteFontStyle style;
55 style.font_weight = base_weight; 92 style.font_weight = base_weight;
56 style.font_slant = base_style; 93 style.font_slant = base_style;
57 style.font_stretch = base_stretch; 94 style.font_stretch = base_stretch;
58 95
59 MapCharactersResult result; 96 MapCharactersResult result;
60 97
61 IPC::Sender* sender = 98 IPC::Sender* sender =
62 sender_override_ ? sender_override_ : ChildThread::Get(); 99 sender_override_ ? sender_override_ : ChildThread::Get();
63 if (!sender->Send(new DWriteFontProxyMsg_MapCharacters( 100 if (!sender->Send(new DWriteFontProxyMsg_MapCharacters(
64 text_chunk, style, locale, source->GetParagraphReadingDirection(), 101 text_chunk, style, locale, source->GetParagraphReadingDirection(),
65 base_family_name ? base_family_name : L"", &result))) 102 base_family_name, &result))) {
103 DCHECK(false);
66 return E_FAIL; 104 return E_FAIL;
105 }
67 106
68 *mapped_length = result.mapped_length; 107 *mapped_length = result.mapped_length;
69 *scale = result.scale; 108 *scale = result.scale;
70 109
71 if (result.family_index == UINT32_MAX) 110 if (result.family_index == UINT32_MAX) {
111 LogFallbackResult(FAILED_NO_FONT);
72 return S_OK; 112 return S_OK;
113 }
73 114
74 mswr::ComPtr<IDWriteFontFamily> family; 115 mswr::ComPtr<IDWriteFontFamily> family;
75 // It would be nice to find a way to determine at runtime if |collection_| is 116 // It would be nice to find a way to determine at runtime if |collection_| is
76 // a proxy collection, or just a generic IDWriteFontCollection. Unfortunately 117 // a proxy collection, or just a generic IDWriteFontCollection. Unfortunately
77 // I can't find a way to get QueryInterface to return the actual class when 118 // I can't find a way to get QueryInterface to return the actual class when
78 // using mswr::RuntimeClass. If we could use QI, we can fallback on 119 // using mswr::RuntimeClass. If we could use QI, we can fallback on
79 // FindFontFamily if the proxy is not available. 120 // FindFontFamily if the proxy is not available.
80 if (!collection_->GetFontFamily(result.family_index, result.family_name, 121 if (!collection_->GetFontFamily(result.family_index, result.family_name,
81 &family)) { 122 &family)) {
82 DCHECK(false); 123 DCHECK(false);
83 return E_FAIL; 124 return E_FAIL;
84 } 125 }
85 126
86 if (FAILED(family->GetFirstMatchingFont( 127 if (FAILED(family->GetFirstMatchingFont(
87 static_cast<DWRITE_FONT_WEIGHT>(result.font_style.font_weight), 128 static_cast<DWRITE_FONT_WEIGHT>(result.font_style.font_weight),
88 static_cast<DWRITE_FONT_STRETCH>(result.font_style.font_stretch), 129 static_cast<DWRITE_FONT_STRETCH>(result.font_style.font_stretch),
89 static_cast<DWRITE_FONT_STYLE>(result.font_style.font_slant), 130 static_cast<DWRITE_FONT_STYLE>(result.font_style.font_slant),
90 mapped_font))) { 131 mapped_font))) {
91 DCHECK(false); 132 DCHECK(false);
92 return E_FAIL; 133 return E_FAIL;
93 } 134 }
94 135
95 DCHECK(*mapped_font); 136 DCHECK(*mapped_font);
96 137 AddCachedFamily(std::move(family), base_family_name);
138 LogFallbackResult(SUCCESS_IPC);
97 return S_OK; 139 return S_OK;
98 } 140 }
99 141
100 HRESULT STDMETHODCALLTYPE 142 HRESULT STDMETHODCALLTYPE
101 FontFallback::RuntimeClassInitialize(DWriteFontCollectionProxy* collection, 143 FontFallback::RuntimeClassInitialize(DWriteFontCollectionProxy* collection,
102 IPC::Sender* sender_override) { 144 IPC::Sender* sender_override) {
103 sender_override_ = sender_override; 145 sender_override_ = sender_override;
104 collection_ = collection; 146 collection_ = collection;
105 return S_OK; 147 return S_OK;
106 } 148 }
107 149
150 bool FontFallback::GetCachedFont(const base::string16& text,
151 const wchar_t* base_family_name,
152 DWRITE_FONT_WEIGHT base_weight,
153 DWRITE_FONT_STYLE base_style,
154 DWRITE_FONT_STRETCH base_stretch,
155 IDWriteFont** font,
156 uint32_t* mapped_length) {
157 std::map<base::string16, std::list<mswr::ComPtr<IDWriteFontFamily>>>::iterator
158 it = fallback_family_cache_.find(base_family_name);
159 if (it == fallback_family_cache_.end())
160 return false;
161
162 TRACE_EVENT0("dwrite", "FontFallback::GetCachedFont");
163
164 std::list<mswr::ComPtr<IDWriteFontFamily>>& family_list = it->second;
165 std::list<mswr::ComPtr<IDWriteFontFamily>>::iterator family_iterator;
166 for (family_iterator = family_list.begin();
167 family_iterator != family_list.end(); ++family_iterator) {
168 mswr::ComPtr<IDWriteFont> matched_font;
169 (*family_iterator)->GetFirstMatchingFont(base_weight, base_stretch,
170 base_style, &matched_font);
171
172 // |character_index| tracks how much of the string we have read. This is
173 // different from |mapped_length| because ReadUnicodeCharacter can andvance
scottmg 2016/04/21 22:45:49 andvance -> advance
Ilya Kulshin 2016/04/22 00:48:37 Done.
174 // |character_index| even if the character cannot be mapped (invalid
175 // surrogate pair or font does not contain a matching glyph).
176 int32_t character_index = 0;
177 uint32_t length = 0; // How much of the text can actually be mapped.
178 while (static_cast<uint32_t>(character_index) < text.length()) {
179 BOOL exists = false;
180 uint32_t character = 0;
181 if (!base::ReadUnicodeCharacter(text.c_str(), text.length(),
182 &character_index, &character))
183 break;
184 if (FAILED(matched_font->HasCharacter(character, &exists)) || !exists)
185 break;
186 character_index++;
187 length = character_index;
188 }
189
190 if (length > 0) {
191 // Move the current family to the front of the list
192 family_list.splice(family_list.begin(), family_list, family_iterator);
193
194 matched_font.CopyTo(font);
195 *mapped_length = length;
196 return true;
197 }
198 }
199
200 return false;
201 }
202
203 void FontFallback::AddCachedFamily(
204 Microsoft::WRL::ComPtr<IDWriteFontFamily> family,
205 const wchar_t* base_family_name) {
206 std::list<mswr::ComPtr<IDWriteFontFamily>>& family_list =
207 fallback_family_cache_[base_family_name];
208 family_list.push_front(std::move(family));
209
210 UMA_HISTOGRAM_COUNTS_100("DirectWrite.Fonts.Proxy.Fallback.CacheSize",
211 family_list.size());
212
213 if (family_list.size() > kMaxFamilyCacheSize)
214 family_list.resize(kMaxFamilyCacheSize);
scottmg 2016/04/21 22:45:49 Can this just be pop_back()? I know the size is on
Ilya Kulshin 2016/04/22 00:48:37 Done.
215 }
216
108 } // namespace content 217 } // namespace content
OLDNEW
« no previous file with comments | « content/child/dwrite_font_proxy/font_fallback_win.h ('k') | content/child/dwrite_font_proxy/font_fallback_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698