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"
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
;
35 // Number of recent GetFontRenderParams() results to cache.
36 const size_t kCacheSize
= 256;
38 // Cached result from a call to GetFontRenderParams().
40 QueryResult(const FontRenderParams
& params
, const std::string
& family
)
46 FontRenderParams params
;
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
57 struct SynchronizedCache
{
58 SynchronizedCache() : cache(kCacheSize
) {}
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
) {
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
) {
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());
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
127 result_pattern
.reset(FcPatternDuplicate(query_pattern
.get()));
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(),
137 result_pattern
.reset(FcFontMatch(NULL
, query_pattern
.get(), &result
));
141 DCHECK(result_pattern
);
144 FcChar8
* family
= NULL
;
145 FcPatternGetString(result_pattern
.get(), FC_FAMILY
, 0, &family
);
147 family_out
->assign(reinterpret_cast<const char*>(family
));
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
) ==
160 params_out
->autohinter
= fc_autohint
;
163 FcBool fc_bitmap
= 0;
164 if (FcPatternGetBool(result_pattern
.get(), FC_EMBEDDED_BITMAP
, 0,
167 params_out
->use_bitmaps
= fc_bitmap
;
170 FcBool fc_hinting
= 0;
171 if (FcPatternGetBool(result_pattern
.get(), FC_HINTING
, 0, &fc_hinting
) ==
173 int fc_hint_style
= FC_HINT_NONE
;
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
) ==
184 params_out
->subpixel_rendering
= ConvertFontconfigRgba(fc_rgba
);
190 // Serialize |query| into a string and hash it to a value suitable for use as a
192 uint32
HashFontRenderParamsQuery(const FontRenderParamsQuery
& query
) {
193 return base::Hash(base::StringPrintf(
194 "%d|%d|%d|%d|%s|%f", query
.for_web_contents
, query
.pixel_size
,
195 query
.point_size
, query
.style
, JoinString(query
.families
, ',').c_str(),
196 query
.device_scale_factor
));
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
;
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
);
212 gfx::Display display
= screen
->GetPrimaryDisplay();
213 actual_query
.device_scale_factor
= display
.device_scale_factor();
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
;
228 *family_out
= result
.family
;
229 return result
.params
;
233 DVLOG(1) << "Computing params for " << hash
;
237 // Start with the delegate's settings, but let Fontconfig have the final say.
238 FontRenderParams params
;
239 const LinuxFontDelegate
* delegate
= LinuxFontDelegate::instance();
241 params
= delegate
->GetDefaultFontRenderParams();
242 QueryFontconfig(actual_query
, ¶ms
, 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;
251 // Fontconfig doesn't support configuring subpixel positioning; check a
253 params
.subpixel_positioning
=
254 actual_query
.for_web_contents
255 ? base::CommandLine::ForCurrentProcess()->HasSwitch(
256 switches::kEnableWebkitTextSubpixelPositioning
)
257 : actual_query
.device_scale_factor
> 1.0f
;
259 // To enable subpixel positioning, we need to disable hinting.
260 if (params
.subpixel_positioning
)
261 params
.hinting
= FontRenderParams::HINTING_NONE
;
264 // Use the first family from the list if Fontconfig didn't suggest a family.
265 if (family_out
&& family_out
->empty() && !actual_query
.families
.empty())
266 *family_out
= actual_query
.families
[0];
269 // Store the result. It's fine if this overwrites a result that was cached
270 // by a different thread in the meantime; the values should be identical.
271 base::AutoLock
lock(synchronized_cache
->lock
);
272 synchronized_cache
->cache
.Put(hash
,
273 QueryResult(params
, family_out
? *family_out
: std::string()));
279 void ClearFontRenderParamsCacheForTest() {
280 SynchronizedCache
* synchronized_cache
= g_synchronized_cache
.Pointer();
281 base::AutoLock
lock(synchronized_cache
->lock
);
282 synchronized_cache
->cache
.Clear();
285 #if defined(OS_CHROMEOS)
286 float GetFontRenderParamsDeviceScaleFactor() {
287 return device_scale_factor_for_internal_display
;
290 void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor
) {
291 device_scale_factor_for_internal_display
= device_scale_factor
;