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/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/synchronization/lock.h"
18 #include "ui/gfx/font.h"
19 #include "ui/gfx/linux_font_delegate.h"
20 #include "ui/gfx/switches.h"
26 #if defined(OS_CHROMEOS)
27 // A device scale factor for an internal display (if any)
28 // that is used to determine if subpixel positioning should be used.
29 float device_scale_factor_for_internal_display
= 1.0f
;
32 // Keyed by hashes of FontRenderParamQuery structs from
33 // HashFontRenderParamsQuery().
34 typedef base::MRUCache
<uint32
, FontRenderParams
> Cache
;
36 // Number of recent GetFontRenderParams() results to cache.
37 const size_t kCacheSize
= 20;
39 // A cache and the lock that must be held while accessing it.
40 // GetFontRenderParams() is called by both the UI thread and the sandbox IPC
42 struct SynchronizedCache
{
43 SynchronizedCache() : cache(kCacheSize
) {}
49 base::LazyInstance
<SynchronizedCache
>::Leaky g_synchronized_cache
=
50 LAZY_INSTANCE_INITIALIZER
;
52 bool IsBrowserTextSubpixelPositioningEnabled() {
53 #if defined(OS_CHROMEOS)
54 return device_scale_factor_for_internal_display
> 1.0f
;
60 // Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
61 FontRenderParams::Hinting
ConvertFontconfigHintStyle(int hint_style
) {
63 case FC_HINT_SLIGHT
: return FontRenderParams::HINTING_SLIGHT
;
64 case FC_HINT_MEDIUM
: return FontRenderParams::HINTING_MEDIUM
;
65 case FC_HINT_FULL
: return FontRenderParams::HINTING_FULL
;
66 default: return FontRenderParams::HINTING_NONE
;
70 // Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
71 FontRenderParams::SubpixelRendering
ConvertFontconfigRgba(int rgba
) {
73 case FC_RGBA_RGB
: return FontRenderParams::SUBPIXEL_RENDERING_RGB
;
74 case FC_RGBA_BGR
: return FontRenderParams::SUBPIXEL_RENDERING_BGR
;
75 case FC_RGBA_VRGB
: return FontRenderParams::SUBPIXEL_RENDERING_VRGB
;
76 case FC_RGBA_VBGR
: return FontRenderParams::SUBPIXEL_RENDERING_VBGR
;
77 default: return FontRenderParams::SUBPIXEL_RENDERING_NONE
;
81 // Queries Fontconfig for rendering settings and updates |params_out| and
82 // |family_out| (if non-NULL). Returns false on failure.
83 bool QueryFontconfig(const FontRenderParamsQuery
& query
,
84 FontRenderParams
* params_out
,
85 std::string
* family_out
) {
86 FcPattern
* pattern
= FcPatternCreate();
89 FcPatternAddBool(pattern
, FC_SCALABLE
, FcTrue
);
91 for (std::vector
<std::string
>::const_iterator it
= query
.families
.begin();
92 it
!= query
.families
.end(); ++it
) {
94 pattern
, FC_FAMILY
, reinterpret_cast<const FcChar8
*>(it
->c_str()));
96 if (query
.pixel_size
> 0)
97 FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, query
.pixel_size
);
98 if (query
.point_size
> 0)
99 FcPatternAddInteger(pattern
, FC_SIZE
, query
.point_size
);
100 if (query
.style
>= 0) {
101 FcPatternAddInteger(pattern
, FC_SLANT
,
102 (query
.style
& Font::ITALIC
) ? FC_SLANT_ITALIC
: FC_SLANT_ROMAN
);
103 FcPatternAddInteger(pattern
, FC_WEIGHT
,
104 (query
.style
& Font::BOLD
) ? FC_WEIGHT_BOLD
: FC_WEIGHT_NORMAL
);
107 FcConfigSubstitute(NULL
, pattern
, FcMatchPattern
);
108 FcDefaultSubstitute(pattern
);
110 FcPattern
* match
= FcFontMatch(NULL
, pattern
, &result
);
111 FcPatternDestroy(pattern
);
116 FcChar8
* family
= NULL
;
117 FcPatternGetString(match
, FC_FAMILY
, 0, &family
);
119 family_out
->assign(reinterpret_cast<const char*>(family
));
123 FcBool fc_antialias
= 0;
124 if (FcPatternGetBool(match
, FC_ANTIALIAS
, 0, &fc_antialias
) ==
126 params_out
->antialiasing
= fc_antialias
;
129 FcBool fc_autohint
= 0;
130 if (FcPatternGetBool(match
, FC_AUTOHINT
, 0, &fc_autohint
) ==
132 params_out
->autohinter
= fc_autohint
;
135 FcBool fc_bitmap
= 0;
136 if (FcPatternGetBool(match
, FC_EMBEDDED_BITMAP
, 0, &fc_bitmap
) ==
138 params_out
->use_bitmaps
= fc_bitmap
;
141 FcBool fc_hinting
= 0;
142 if (FcPatternGetBool(match
, FC_HINTING
, 0, &fc_hinting
) == FcResultMatch
) {
143 int fc_hint_style
= FC_HINT_NONE
;
145 FcPatternGetInteger(match
, FC_HINT_STYLE
, 0, &fc_hint_style
);
146 params_out
->hinting
= ConvertFontconfigHintStyle(fc_hint_style
);
149 int fc_rgba
= FC_RGBA_NONE
;
150 if (FcPatternGetInteger(match
, FC_RGBA
, 0, &fc_rgba
) == FcResultMatch
)
151 params_out
->subpixel_rendering
= ConvertFontconfigRgba(fc_rgba
);
154 FcPatternDestroy(match
);
158 // Serialize |query| into a string and hash it to a value suitable for use as a
160 uint32
HashFontRenderParamsQuery(const FontRenderParamsQuery
& query
) {
161 return base::Hash(base::StringPrintf("%d|%d|%d|%d|%s",
162 query
.for_web_contents
, query
.pixel_size
, query
.point_size
, query
.style
,
163 JoinString(query
.families
, ',').c_str()));
168 FontRenderParams
GetFontRenderParams(const FontRenderParamsQuery
& query
,
169 std::string
* family_out
) {
170 const uint32 hash
= HashFontRenderParamsQuery(query
);
172 // The family returned by Fontconfig isn't part of FontRenderParams, so we
173 // can only return a value from the cache if it wasn't requested.
174 SynchronizedCache
* synchronized_cache
= g_synchronized_cache
.Pointer();
175 base::AutoLock
lock(synchronized_cache
->lock
);
176 Cache::const_iterator it
= synchronized_cache
->cache
.Get(hash
);
177 if (it
!= synchronized_cache
->cache
.end()) {
178 DVLOG(1) << "Returning cached params for " << hash
;
184 DVLOG(1) << "Computing params for " << hash
185 << (family_out
? " (family requested)" : "");
187 // Start with the delegate's settings, but let Fontconfig have the final say.
188 FontRenderParams params
;
189 const LinuxFontDelegate
* delegate
= LinuxFontDelegate::instance();
191 params
= delegate
->GetDefaultFontRenderParams();
192 QueryFontconfig(query
, ¶ms
, family_out
);
193 if (!params
.antialiasing
) {
194 // Cairo forces full hinting when antialiasing is disabled, since anything
195 // less than that looks awful; do the same here. Requesting subpixel
196 // rendering or positioning doesn't make sense either.
197 params
.hinting
= FontRenderParams::HINTING_FULL
;
198 params
.subpixel_rendering
= FontRenderParams::SUBPIXEL_RENDERING_NONE
;
199 params
.subpixel_positioning
= false;
201 // Fontconfig doesn't support configuring subpixel positioning; check a
203 params
.subpixel_positioning
=
204 query
.for_web_contents
?
205 CommandLine::ForCurrentProcess()->HasSwitch(
206 switches::kEnableWebkitTextSubpixelPositioning
) :
207 IsBrowserTextSubpixelPositioningEnabled();
209 // To enable subpixel positioning, we need to disable hinting.
210 if (params
.subpixel_positioning
)
211 params
.hinting
= FontRenderParams::HINTING_NONE
;
214 // Use the first family from the list if Fontconfig didn't suggest a family.
215 if (family_out
&& family_out
->empty() && !query
.families
.empty())
216 *family_out
= query
.families
[0];
218 // Store the computed struct. It's fine if this overwrites a struct that was
219 // cached by a different thread in the meantime; the values should be
221 SynchronizedCache
* synchronized_cache
= g_synchronized_cache
.Pointer();
222 base::AutoLock
lock(synchronized_cache
->lock
);
223 synchronized_cache
->cache
.Put(hash
, params
);
228 void ClearFontRenderParamsCacheForTest() {
229 SynchronizedCache
* synchronized_cache
= g_synchronized_cache
.Pointer();
230 base::AutoLock
lock(synchronized_cache
->lock
);
231 synchronized_cache
->cache
.Clear();
234 #if defined(OS_CHROMEOS)
235 void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor
) {
236 device_scale_factor_for_internal_display
= device_scale_factor
;