1 // Copyright (c) 2012 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 "ui/gfx/font_render_params.h"
6
7 #include <fontconfig/fontconfig.h>
8 #include <stddef.h>
9 #include <stdint.h>
10
11 #include <memory>
12
13 #include "base/command_line.h"
14 #include "base/containers/mru_cache.h"
15 #include "base/hash/hash.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/synchronization/lock.h"
22 #include "base/trace_event/trace_event.h"
23 #include "build/build_config.h"
24 #include "ui/gfx/font.h"
25 #include "ui/gfx/linux/fontconfig_util.h"
26 #include "ui/gfx/skia_font_delegate.h"
27 #include "ui/gfx/switches.h"
28
29 namespace gfx {
30
31 namespace {
32
FontWeightToFCWeight(Font::Weight weight)33 int FontWeightToFCWeight(Font::Weight weight) {
34 const int weight_number = static_cast<int>(weight);
35 if (weight_number <= (static_cast<int>(Font::Weight::THIN) +
36 static_cast<int>(Font::Weight::EXTRA_LIGHT)) /
37 2)
38 return FC_WEIGHT_THIN;
39 else if (weight_number <= (static_cast<int>(Font::Weight::EXTRA_LIGHT) +
40 static_cast<int>(Font::Weight::LIGHT)) /
41 2)
42 return FC_WEIGHT_ULTRALIGHT;
43 else if (weight_number <= (static_cast<int>(Font::Weight::LIGHT) +
44 static_cast<int>(Font::Weight::NORMAL)) /
45 2)
46 return FC_WEIGHT_LIGHT;
47 else if (weight_number <= (static_cast<int>(Font::Weight::NORMAL) +
48 static_cast<int>(Font::Weight::MEDIUM)) /
49 2)
50 return FC_WEIGHT_NORMAL;
51 else if (weight_number <= (static_cast<int>(Font::Weight::MEDIUM) +
52 static_cast<int>(Font::Weight::SEMIBOLD)) /
53 2)
54 return FC_WEIGHT_MEDIUM;
55 else if (weight_number <= (static_cast<int>(Font::Weight::SEMIBOLD) +
56 static_cast<int>(Font::Weight::BOLD)) /
57 2)
58 return FC_WEIGHT_DEMIBOLD;
59 else if (weight_number <= (static_cast<int>(Font::Weight::BOLD) +
60 static_cast<int>(Font::Weight::EXTRA_BOLD)) /
61 2)
62 return FC_WEIGHT_BOLD;
63 else if (weight_number <= (static_cast<int>(Font::Weight::EXTRA_BOLD) +
64 static_cast<int>(Font::Weight::BLACK)) /
65 2)
66 return FC_WEIGHT_ULTRABOLD;
67 else
68 return FC_WEIGHT_BLACK;
69 }
70
71 // A device scale factor used to determine if subpixel positioning
72 // should be used.
73 float device_scale_factor_ = 1.0f;
74
75 // Number of recent GetFontRenderParams() results to cache.
76 const size_t kCacheSize = 256;
77
78 // Cached result from a call to GetFontRenderParams().
79 struct QueryResult {
QueryResultgfx::__anondb39d85d0111::QueryResult80 QueryResult(const FontRenderParams& params, const std::string& family)
81 : params(params),
82 family(family) {
83 }
~QueryResultgfx::__anondb39d85d0111::QueryResult84 ~QueryResult() {}
85
86 FontRenderParams params;
87 std::string family;
88 };
89
90 // Keyed by hashes of FontRenderParamQuery structs from
91 // HashFontRenderParamsQuery().
92 typedef base::MRUCache<uint32_t, QueryResult> Cache;
93
94 // A cache and the lock that must be held while accessing it.
95 // GetFontRenderParams() is called by both the UI thread and the sandbox IPC
96 // thread.
97 struct SynchronizedCache {
SynchronizedCachegfx::__anondb39d85d0111::SynchronizedCache98 SynchronizedCache() : cache(kCacheSize) {}
99
100 base::Lock lock;
101 Cache cache;
102 };
103
104 base::LazyInstance<SynchronizedCache>::Leaky g_synchronized_cache =
105 LAZY_INSTANCE_INITIALIZER;
106
107 // Queries Fontconfig for rendering settings and updates |params_out| and
108 // |family_out| (if non-NULL). Returns false on failure.
QueryFontconfig(const FontRenderParamsQuery & query,FontRenderParams * params_out,std::string * family_out)109 bool QueryFontconfig(const FontRenderParamsQuery& query,
110 FontRenderParams* params_out,
111 std::string* family_out) {
112 TRACE_EVENT0("fonts", "gfx::QueryFontconfig");
113
114 ScopedFcPattern query_pattern(FcPatternCreate());
115 CHECK(query_pattern);
116
117 FcPatternAddBool(query_pattern.get(), FC_SCALABLE, FcTrue);
118
119 for (auto it = query.families.begin(); it != query.families.end(); ++it) {
120 FcPatternAddString(query_pattern.get(),
121 FC_FAMILY, reinterpret_cast<const FcChar8*>(it->c_str()));
122 }
123 if (query.pixel_size > 0)
124 FcPatternAddDouble(query_pattern.get(), FC_PIXEL_SIZE, query.pixel_size);
125 if (query.point_size > 0)
126 FcPatternAddInteger(query_pattern.get(), FC_SIZE, query.point_size);
127 if (query.style >= 0) {
128 FcPatternAddInteger(query_pattern.get(), FC_SLANT,
129 (query.style & Font::ITALIC) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
130 }
131 if (query.weight != Font::Weight::INVALID) {
132 FcPatternAddInteger(query_pattern.get(), FC_WEIGHT,
133 FontWeightToFCWeight(query.weight));
134 }
135
136 FcConfig* config = GetGlobalFontConfig();
137 FcConfigSubstitute(config, query_pattern.get(), FcMatchPattern);
138 FcDefaultSubstitute(query_pattern.get());
139
140 ScopedFcPattern result_pattern;
141 if (query.is_empty()) {
142 // If the query was empty, call FcConfigSubstituteWithPat() to get a
143 // non-family- or size-specific configuration so it can be used as the
144 // default.
145 result_pattern.reset(FcPatternDuplicate(query_pattern.get()));
146 if (!result_pattern)
147 return false;
148 FcPatternDel(result_pattern.get(), FC_FAMILY);
149 FcPatternDel(result_pattern.get(), FC_PIXEL_SIZE);
150 FcPatternDel(result_pattern.get(), FC_SIZE);
151 FcConfigSubstituteWithPat(config, result_pattern.get(), query_pattern.get(),
152 FcMatchFont);
153 } else {
154 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("fonts"), "FcFontMatch");
155 FcResult result;
156 result_pattern.reset(FcFontMatch(config, query_pattern.get(), &result));
157 if (!result_pattern)
158 return false;
159 }
160 DCHECK(result_pattern);
161
162 if (family_out) {
163 FcChar8* family = NULL;
164 FcPatternGetString(result_pattern.get(), FC_FAMILY, 0, &family);
165 if (family)
166 family_out->assign(reinterpret_cast<const char*>(family));
167 }
168
169 if (params_out)
170 GetFontRenderParamsFromFcPattern(result_pattern.get(), params_out);
171
172 return true;
173 }
174
175 // Serialize |query| into a string and hash it to a value suitable for use as a
176 // cache key.
HashFontRenderParamsQuery(const FontRenderParamsQuery & query)177 uint32_t HashFontRenderParamsQuery(const FontRenderParamsQuery& query) {
178 return base::Hash(base::StringPrintf(
179 "%d|%d|%d|%d|%s|%f", query.pixel_size, query.point_size, query.style,
180 static_cast<int>(query.weight),
181 base::JoinString(query.families, ",").c_str(),
182 query.device_scale_factor));
183 }
184
185 } // namespace
186
GetFontRenderParams(const FontRenderParamsQuery & query,std::string * family_out)187 FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query,
188 std::string* family_out) {
189 TRACE_EVENT0("fonts", "gfx::GetFontRenderParams");
190
191 FontRenderParamsQuery actual_query(query);
192 if (actual_query.device_scale_factor == 0)
193 actual_query.device_scale_factor = device_scale_factor_;
194
195 const uint32_t hash = HashFontRenderParamsQuery(actual_query);
196 SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
197
198 {
199 // Try to find a cached result so Fontconfig doesn't need to be queried.
200 base::AutoLock lock(synchronized_cache->lock);
201 Cache::const_iterator it = synchronized_cache->cache.Get(hash);
202 if (it != synchronized_cache->cache.end()) {
203 DVLOG(1) << "Returning cached params for " << hash;
204 const QueryResult& result = it->second;
205 if (family_out)
206 *family_out = result.family;
207 return result.params;
208 }
209 }
210
211 DVLOG(1) << "Computing params for " << hash;
212 if (family_out)
213 family_out->clear();
214
215 // Start with the delegate's settings, but let Fontconfig have the final say.
216 FontRenderParams params;
217 const SkiaFontDelegate* delegate = SkiaFontDelegate::instance();
218 if (delegate)
219 params = delegate->GetDefaultFontRenderParams();
220 QueryFontconfig(actual_query, ¶ms, family_out);
221 if (!params.antialiasing) {
222 // Cairo forces full hinting when antialiasing is disabled, since anything
223 // less than that looks awful; do the same here. Requesting subpixel
224 // rendering or positioning doesn't make sense either.
225 params.hinting = FontRenderParams::HINTING_FULL;
226 params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
227 params.subpixel_positioning = false;
228 } else if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
229 switches::kDisableFontSubpixelPositioning)) {
230 #if !defined(OS_CHROMEOS)
231 params.subpixel_positioning = actual_query.device_scale_factor > 1.0f;
232 #else
233 // We want to enable subpixel positioning for fractional dsf.
234 params.subpixel_positioning =
235 std::abs(std::round(actual_query.device_scale_factor) -
236 actual_query.device_scale_factor) >
237 std::numeric_limits<float>::epsilon();
238 #endif // !defined(OS_CHROMEOS)
239
240 // To enable subpixel positioning, we need to disable hinting.
241 if (params.subpixel_positioning)
242 params.hinting = FontRenderParams::HINTING_NONE;
243 }
244
245 // Use the first family from the list if Fontconfig didn't suggest a family.
246 if (family_out && family_out->empty() && !actual_query.families.empty())
247 *family_out = actual_query.families[0];
248
249 {
250 // Store the result. It's fine if this overwrites a result that was cached
251 // by a different thread in the meantime; the values should be identical.
252 base::AutoLock lock(synchronized_cache->lock);
253 synchronized_cache->cache.Put(hash,
254 QueryResult(params, family_out ? *family_out : std::string()));
255 }
256
257 return params;
258 }
259
ClearFontRenderParamsCacheForTest()260 void ClearFontRenderParamsCacheForTest() {
261 SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
262 base::AutoLock lock(synchronized_cache->lock);
263 synchronized_cache->cache.Clear();
264 }
265
GetFontRenderParamsDeviceScaleFactor()266 float GetFontRenderParamsDeviceScaleFactor() {
267 return device_scale_factor_;
268 }
269
SetFontRenderParamsDeviceScaleFactor(float device_scale_factor)270 void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor) {
271 device_scale_factor_ = device_scale_factor;
272 }
273
274 } // namespace gfx
275