• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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"
21 
22 namespace gfx {
23 
24 namespace {
25 
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;
30 #endif
31 
32 // Keyed by hashes of FontRenderParamQuery structs from
33 // HashFontRenderParamsQuery().
34 typedef base::MRUCache<uint32, FontRenderParams> Cache;
35 
36 // Number of recent GetFontRenderParams() results to cache.
37 const size_t kCacheSize = 20;
38 
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
41 // thread.
42 struct SynchronizedCache {
SynchronizedCachegfx::__anon88c1421f0111::SynchronizedCache43   SynchronizedCache() : cache(kCacheSize) {}
44 
45   base::Lock lock;
46   Cache cache;
47 };
48 
49 base::LazyInstance<SynchronizedCache>::Leaky g_synchronized_cache =
50     LAZY_INSTANCE_INITIALIZER;
51 
IsBrowserTextSubpixelPositioningEnabled()52 bool IsBrowserTextSubpixelPositioningEnabled() {
53 #if defined(OS_CHROMEOS)
54   return device_scale_factor_for_internal_display > 1.0f;
55 #else
56   return false;
57 #endif
58 }
59 
60 // Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
ConvertFontconfigHintStyle(int hint_style)61 FontRenderParams::Hinting ConvertFontconfigHintStyle(int hint_style) {
62   switch (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;
67   }
68 }
69 
70 // Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
ConvertFontconfigRgba(int rgba)71 FontRenderParams::SubpixelRendering ConvertFontconfigRgba(int rgba) {
72   switch (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;
78   }
79 }
80 
81 // Queries Fontconfig for rendering settings and updates |params_out| and
82 // |family_out| (if non-NULL). Returns false on failure.
QueryFontconfig(const FontRenderParamsQuery & query,FontRenderParams * params_out,std::string * family_out)83 bool QueryFontconfig(const FontRenderParamsQuery& query,
84                      FontRenderParams* params_out,
85                      std::string* family_out) {
86   FcPattern* pattern = FcPatternCreate();
87   CHECK(pattern);
88 
89   FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
90 
91   for (std::vector<std::string>::const_iterator it = query.families.begin();
92        it != query.families.end(); ++it) {
93     FcPatternAddString(
94         pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(it->c_str()));
95   }
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);
105   }
106 
107   FcConfigSubstitute(NULL, pattern, FcMatchPattern);
108   FcDefaultSubstitute(pattern);
109   FcResult result;
110   FcPattern* match = FcFontMatch(NULL, pattern, &result);
111   FcPatternDestroy(pattern);
112   if (!match)
113     return false;
114 
115   if (family_out) {
116     FcChar8* family = NULL;
117     FcPatternGetString(match, FC_FAMILY, 0, &family);
118     if (family)
119       family_out->assign(reinterpret_cast<const char*>(family));
120   }
121 
122   if (params_out) {
123     FcBool fc_antialias = 0;
124     if (FcPatternGetBool(match, FC_ANTIALIAS, 0, &fc_antialias) ==
125         FcResultMatch) {
126       params_out->antialiasing = fc_antialias;
127     }
128 
129     FcBool fc_autohint = 0;
130     if (FcPatternGetBool(match, FC_AUTOHINT, 0, &fc_autohint) ==
131         FcResultMatch) {
132       params_out->autohinter = fc_autohint;
133     }
134 
135     FcBool fc_bitmap = 0;
136     if (FcPatternGetBool(match, FC_EMBEDDED_BITMAP, 0, &fc_bitmap) ==
137         FcResultMatch) {
138       params_out->use_bitmaps = fc_bitmap;
139     }
140 
141     FcBool fc_hinting = 0;
142     if (FcPatternGetBool(match, FC_HINTING, 0, &fc_hinting) == FcResultMatch) {
143       int fc_hint_style = FC_HINT_NONE;
144       if (fc_hinting)
145         FcPatternGetInteger(match, FC_HINT_STYLE, 0, &fc_hint_style);
146       params_out->hinting = ConvertFontconfigHintStyle(fc_hint_style);
147     }
148 
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);
152   }
153 
154   FcPatternDestroy(match);
155   return true;
156 }
157 
158 // Serialize |query| into a string and hash it to a value suitable for use as a
159 // cache key.
HashFontRenderParamsQuery(const FontRenderParamsQuery & query)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()));
164 }
165 
166 }  // namespace
167 
GetFontRenderParams(const FontRenderParamsQuery & query,std::string * family_out)168 FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query,
169                                      std::string* family_out) {
170   const uint32 hash = HashFontRenderParamsQuery(query);
171   if (!family_out) {
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;
179       return it->second;
180     }
181   } else {
182     family_out->clear();
183   }
184   DVLOG(1) << "Computing params for " << hash
185            << (family_out ? " (family requested)" : "");
186 
187   // Start with the delegate's settings, but let Fontconfig have the final say.
188   FontRenderParams params;
189   const LinuxFontDelegate* delegate = LinuxFontDelegate::instance();
190   if (delegate)
191     params = delegate->GetDefaultFontRenderParams();
192   QueryFontconfig(query, &params, 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;
200   } else {
201     // Fontconfig doesn't support configuring subpixel positioning; check a
202     // flag.
203     params.subpixel_positioning =
204         query.for_web_contents ?
205         CommandLine::ForCurrentProcess()->HasSwitch(
206             switches::kEnableWebkitTextSubpixelPositioning) :
207         IsBrowserTextSubpixelPositioningEnabled();
208 
209     // To enable subpixel positioning, we need to disable hinting.
210     if (params.subpixel_positioning)
211       params.hinting = FontRenderParams::HINTING_NONE;
212   }
213 
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];
217 
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
220   // identical.
221   SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
222   base::AutoLock lock(synchronized_cache->lock);
223   synchronized_cache->cache.Put(hash, params);
224 
225   return params;
226 }
227 
ClearFontRenderParamsCacheForTest()228 void ClearFontRenderParamsCacheForTest() {
229   SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
230   base::AutoLock lock(synchronized_cache->lock);
231   synchronized_cache->cache.Clear();
232 }
233 
234 #if defined(OS_CHROMEOS)
SetFontRenderParamsDeviceScaleFactor(float device_scale_factor)235 void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor) {
236   device_scale_factor_for_internal_display = device_scale_factor;
237 }
238 #endif
239 
240 }  // namespace gfx
241