Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / gfx / font_render_params_linux.cc
blobb62c5252fee4326573695906ce888b6ef38dadbf
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.
5 #include "ui/gfx/font_render_params.h"
7 #include <fontconfig/fontconfig.h>
9 #include "base/command_line.h"
10 #include "base/containers/mru_cache.h"
11 #include "base/hash.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/lock.h"
19 #include "ui/gfx/display.h"
20 #include "ui/gfx/font.h"
21 #include "ui/gfx/linux_font_delegate.h"
22 #include "ui/gfx/screen.h"
23 #include "ui/gfx/switches.h"
25 namespace gfx {
27 namespace {
29 #if defined(OS_CHROMEOS)
30 // A device scale factor for an internal display (if any)
31 // that is used to determine if subpixel positioning should be used.
32 float device_scale_factor_for_internal_display = 1.0f;
33 #endif
35 // Number of recent GetFontRenderParams() results to cache.
36 const size_t kCacheSize = 256;
38 // Cached result from a call to GetFontRenderParams().
39 struct QueryResult {
40 QueryResult(const FontRenderParams& params, const std::string& family)
41 : params(params),
42 family(family) {
44 ~QueryResult() {}
46 FontRenderParams params;
47 std::string family;
50 // Keyed by hashes of FontRenderParamQuery structs from
51 // HashFontRenderParamsQuery().
52 typedef base::MRUCache<uint32, QueryResult> Cache;
54 // A cache and the lock that must be held while accessing it.
55 // GetFontRenderParams() is called by both the UI thread and the sandbox IPC
56 // thread.
57 struct SynchronizedCache {
58 SynchronizedCache() : cache(kCacheSize) {}
60 base::Lock lock;
61 Cache cache;
64 base::LazyInstance<SynchronizedCache>::Leaky g_synchronized_cache =
65 LAZY_INSTANCE_INITIALIZER;
67 // Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
68 FontRenderParams::Hinting ConvertFontconfigHintStyle(int hint_style) {
69 switch (hint_style) {
70 case FC_HINT_SLIGHT: return FontRenderParams::HINTING_SLIGHT;
71 case FC_HINT_MEDIUM: return FontRenderParams::HINTING_MEDIUM;
72 case FC_HINT_FULL: return FontRenderParams::HINTING_FULL;
73 default: return FontRenderParams::HINTING_NONE;
77 // Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
78 FontRenderParams::SubpixelRendering ConvertFontconfigRgba(int rgba) {
79 switch (rgba) {
80 case FC_RGBA_RGB: return FontRenderParams::SUBPIXEL_RENDERING_RGB;
81 case FC_RGBA_BGR: return FontRenderParams::SUBPIXEL_RENDERING_BGR;
82 case FC_RGBA_VRGB: return FontRenderParams::SUBPIXEL_RENDERING_VRGB;
83 case FC_RGBA_VBGR: return FontRenderParams::SUBPIXEL_RENDERING_VBGR;
84 default: return FontRenderParams::SUBPIXEL_RENDERING_NONE;
88 // Queries Fontconfig for rendering settings and updates |params_out| and
89 // |family_out| (if non-NULL). Returns false on failure.
90 bool QueryFontconfig(const FontRenderParamsQuery& query,
91 FontRenderParams* params_out,
92 std::string* family_out) {
93 struct FcPatternDeleter {
94 void operator()(FcPattern* ptr) const { FcPatternDestroy(ptr); }
96 typedef scoped_ptr<FcPattern, FcPatternDeleter> ScopedFcPattern;
98 ScopedFcPattern query_pattern(FcPatternCreate());
99 CHECK(query_pattern);
101 FcPatternAddBool(query_pattern.get(), FC_SCALABLE, FcTrue);
103 for (std::vector<std::string>::const_iterator it = query.families.begin();
104 it != query.families.end(); ++it) {
105 FcPatternAddString(query_pattern.get(),
106 FC_FAMILY, reinterpret_cast<const FcChar8*>(it->c_str()));
108 if (query.pixel_size > 0)
109 FcPatternAddDouble(query_pattern.get(), FC_PIXEL_SIZE, query.pixel_size);
110 if (query.point_size > 0)
111 FcPatternAddInteger(query_pattern.get(), FC_SIZE, query.point_size);
112 if (query.style >= 0) {
113 FcPatternAddInteger(query_pattern.get(), FC_SLANT,
114 (query.style & Font::ITALIC) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
115 FcPatternAddInteger(query_pattern.get(), FC_WEIGHT,
116 (query.style & Font::BOLD) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL);
119 FcConfigSubstitute(NULL, query_pattern.get(), FcMatchPattern);
120 FcDefaultSubstitute(query_pattern.get());
122 ScopedFcPattern result_pattern;
123 if (query.is_empty()) {
124 // If the query was empty, call FcConfigSubstituteWithPat() to get a
125 // non-family- or size-specific configuration so it can be used as the
126 // default.
127 result_pattern.reset(FcPatternDuplicate(query_pattern.get()));
128 if (!result_pattern)
129 return false;
130 FcPatternDel(result_pattern.get(), FC_FAMILY);
131 FcPatternDel(result_pattern.get(), FC_PIXEL_SIZE);
132 FcPatternDel(result_pattern.get(), FC_SIZE);
133 FcConfigSubstituteWithPat(NULL, result_pattern.get(), query_pattern.get(),
134 FcMatchFont);
135 } else {
136 FcResult result;
137 result_pattern.reset(FcFontMatch(NULL, query_pattern.get(), &result));
138 if (!result_pattern)
139 return false;
141 DCHECK(result_pattern);
143 if (family_out) {
144 FcChar8* family = NULL;
145 FcPatternGetString(result_pattern.get(), FC_FAMILY, 0, &family);
146 if (family)
147 family_out->assign(reinterpret_cast<const char*>(family));
150 if (params_out) {
151 FcBool fc_antialias = 0;
152 if (FcPatternGetBool(result_pattern.get(), FC_ANTIALIAS, 0,
153 &fc_antialias) == FcResultMatch) {
154 params_out->antialiasing = fc_antialias;
157 FcBool fc_autohint = 0;
158 if (FcPatternGetBool(result_pattern.get(), FC_AUTOHINT, 0, &fc_autohint) ==
159 FcResultMatch) {
160 params_out->autohinter = fc_autohint;
163 FcBool fc_bitmap = 0;
164 if (FcPatternGetBool(result_pattern.get(), FC_EMBEDDED_BITMAP, 0,
165 &fc_bitmap) ==
166 FcResultMatch) {
167 params_out->use_bitmaps = fc_bitmap;
170 FcBool fc_hinting = 0;
171 if (FcPatternGetBool(result_pattern.get(), FC_HINTING, 0, &fc_hinting) ==
172 FcResultMatch) {
173 int fc_hint_style = FC_HINT_NONE;
174 if (fc_hinting) {
175 FcPatternGetInteger(
176 result_pattern.get(), FC_HINT_STYLE, 0, &fc_hint_style);
178 params_out->hinting = ConvertFontconfigHintStyle(fc_hint_style);
181 int fc_rgba = FC_RGBA_NONE;
182 if (FcPatternGetInteger(result_pattern.get(), FC_RGBA, 0, &fc_rgba) ==
183 FcResultMatch)
184 params_out->subpixel_rendering = ConvertFontconfigRgba(fc_rgba);
187 return true;
190 // Serialize |query| into a string and hash it to a value suitable for use as a
191 // cache key.
192 uint32 HashFontRenderParamsQuery(const FontRenderParamsQuery& query) {
193 return base::Hash(base::StringPrintf(
194 "%d|%d|%d|%s|%f", query.pixel_size, query.point_size, query.style,
195 base::JoinString(query.families, ",").c_str(),
196 query.device_scale_factor));
199 } // namespace
201 FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query,
202 std::string* family_out) {
203 FontRenderParamsQuery actual_query(query);
204 if (actual_query.device_scale_factor == 0) {
205 #if defined(OS_CHROMEOS)
206 actual_query.device_scale_factor = device_scale_factor_for_internal_display;
207 #else
208 // Linux does not support per-display DPI, so we use a slightly simpler
209 // code path than on Chrome OS to figure out the device scale factor.
210 gfx::Screen* screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE);
211 if (screen) {
212 gfx::Display display = screen->GetPrimaryDisplay();
213 actual_query.device_scale_factor = display.device_scale_factor();
215 #endif
217 const uint32 hash = HashFontRenderParamsQuery(actual_query);
218 SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
221 // Try to find a cached result so Fontconfig doesn't need to be queried.
222 base::AutoLock lock(synchronized_cache->lock);
223 Cache::const_iterator it = synchronized_cache->cache.Get(hash);
224 if (it != synchronized_cache->cache.end()) {
225 DVLOG(1) << "Returning cached params for " << hash;
226 const QueryResult& result = it->second;
227 if (family_out)
228 *family_out = result.family;
229 return result.params;
233 DVLOG(1) << "Computing params for " << hash;
234 if (family_out)
235 family_out->clear();
237 // Start with the delegate's settings, but let Fontconfig have the final say.
238 FontRenderParams params;
239 const LinuxFontDelegate* delegate = LinuxFontDelegate::instance();
240 if (delegate)
241 params = delegate->GetDefaultFontRenderParams();
242 QueryFontconfig(actual_query, &params, family_out);
243 if (!params.antialiasing) {
244 // Cairo forces full hinting when antialiasing is disabled, since anything
245 // less than that looks awful; do the same here. Requesting subpixel
246 // rendering or positioning doesn't make sense either.
247 params.hinting = FontRenderParams::HINTING_FULL;
248 params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
249 params.subpixel_positioning = false;
250 } else {
251 params.subpixel_positioning = actual_query.device_scale_factor > 1.0f;
253 // To enable subpixel positioning, we need to disable hinting.
254 if (params.subpixel_positioning)
255 params.hinting = FontRenderParams::HINTING_NONE;
258 // Use the first family from the list if Fontconfig didn't suggest a family.
259 if (family_out && family_out->empty() && !actual_query.families.empty())
260 *family_out = actual_query.families[0];
263 // Store the result. It's fine if this overwrites a result that was cached
264 // by a different thread in the meantime; the values should be identical.
265 base::AutoLock lock(synchronized_cache->lock);
266 synchronized_cache->cache.Put(hash,
267 QueryResult(params, family_out ? *family_out : std::string()));
270 return params;
273 void ClearFontRenderParamsCacheForTest() {
274 SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
275 base::AutoLock lock(synchronized_cache->lock);
276 synchronized_cache->cache.Clear();
279 #if defined(OS_CHROMEOS)
280 float GetFontRenderParamsDeviceScaleFactor() {
281 return device_scale_factor_for_internal_display;
284 void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor) {
285 device_scale_factor_for_internal_display = device_scale_factor;
287 #endif
289 } // namespace gfx