Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / ui / gfx / font_render_params_linux.cc
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 // Number of recent GetFontRenderParams() results to cache.
33 const size_t kCacheSize = 20;
34
35 // Cached result from a call to GetFontRenderParams().
36 struct QueryResult {
37   QueryResult(const FontRenderParams& params, const std::string& family)
38       : params(params),
39         family(family) {
40   }
41   ~QueryResult() {}
42
43   FontRenderParams params;
44   std::string family;
45 };
46
47 // Keyed by hashes of FontRenderParamQuery structs from
48 // HashFontRenderParamsQuery().
49 typedef base::MRUCache<uint32, QueryResult> Cache;
50
51 // A cache and the lock that must be held while accessing it.
52 // GetFontRenderParams() is called by both the UI thread and the sandbox IPC
53 // thread.
54 struct SynchronizedCache {
55   SynchronizedCache() : cache(kCacheSize) {}
56
57   base::Lock lock;
58   Cache cache;
59 };
60
61 base::LazyInstance<SynchronizedCache>::Leaky g_synchronized_cache =
62     LAZY_INSTANCE_INITIALIZER;
63
64 bool IsBrowserTextSubpixelPositioningEnabled() {
65 #if defined(OS_CHROMEOS)
66   return device_scale_factor_for_internal_display > 1.0f;
67 #else
68   return false;
69 #endif
70 }
71
72 // Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
73 FontRenderParams::Hinting ConvertFontconfigHintStyle(int hint_style) {
74   switch (hint_style) {
75     case FC_HINT_SLIGHT: return FontRenderParams::HINTING_SLIGHT;
76     case FC_HINT_MEDIUM: return FontRenderParams::HINTING_MEDIUM;
77     case FC_HINT_FULL:   return FontRenderParams::HINTING_FULL;
78     default:             return FontRenderParams::HINTING_NONE;
79   }
80 }
81
82 // Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
83 FontRenderParams::SubpixelRendering ConvertFontconfigRgba(int rgba) {
84   switch (rgba) {
85     case FC_RGBA_RGB:  return FontRenderParams::SUBPIXEL_RENDERING_RGB;
86     case FC_RGBA_BGR:  return FontRenderParams::SUBPIXEL_RENDERING_BGR;
87     case FC_RGBA_VRGB: return FontRenderParams::SUBPIXEL_RENDERING_VRGB;
88     case FC_RGBA_VBGR: return FontRenderParams::SUBPIXEL_RENDERING_VBGR;
89     default:           return FontRenderParams::SUBPIXEL_RENDERING_NONE;
90   }
91 }
92
93 // Queries Fontconfig for rendering settings and updates |params_out| and
94 // |family_out| (if non-NULL). Returns false on failure.
95 bool QueryFontconfig(const FontRenderParamsQuery& query,
96                      FontRenderParams* params_out,
97                      std::string* family_out) {
98   FcPattern* query_pattern = FcPatternCreate();
99   CHECK(query_pattern);
100
101   FcPatternAddBool(query_pattern, FC_SCALABLE, FcTrue);
102
103   for (std::vector<std::string>::const_iterator it = query.families.begin();
104        it != query.families.end(); ++it) {
105     FcPatternAddString(query_pattern,
106         FC_FAMILY, reinterpret_cast<const FcChar8*>(it->c_str()));
107   }
108   if (query.pixel_size > 0)
109     FcPatternAddDouble(query_pattern, FC_PIXEL_SIZE, query.pixel_size);
110   if (query.point_size > 0)
111     FcPatternAddInteger(query_pattern, FC_SIZE, query.point_size);
112   if (query.style >= 0) {
113     FcPatternAddInteger(query_pattern, FC_SLANT,
114         (query.style & Font::ITALIC) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
115     FcPatternAddInteger(query_pattern, FC_WEIGHT,
116         (query.style & Font::BOLD) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL);
117   }
118
119   FcConfigSubstitute(NULL, query_pattern, FcMatchPattern);
120   FcDefaultSubstitute(query_pattern);
121
122   // If the query was non-empty, match a specific font and destroy the query
123   // pattern. Otherwise, just use the query pattern.
124   FcPattern* result_pattern = query_pattern;
125   if (!query.is_empty()) {
126     FcResult result;
127     result_pattern = FcFontMatch(NULL, query_pattern, &result);
128     FcPatternDestroy(query_pattern);
129     query_pattern = NULL;
130     if (!result_pattern)
131       return false;
132   }
133
134   if (family_out) {
135     FcChar8* family = NULL;
136     FcPatternGetString(result_pattern, FC_FAMILY, 0, &family);
137     if (family)
138       family_out->assign(reinterpret_cast<const char*>(family));
139   }
140
141   if (params_out) {
142     FcBool fc_antialias = 0;
143     if (FcPatternGetBool(result_pattern, FC_ANTIALIAS, 0, &fc_antialias) ==
144         FcResultMatch) {
145       params_out->antialiasing = fc_antialias;
146     }
147
148     FcBool fc_autohint = 0;
149     if (FcPatternGetBool(result_pattern, FC_AUTOHINT, 0, &fc_autohint) ==
150         FcResultMatch) {
151       params_out->autohinter = fc_autohint;
152     }
153
154     FcBool fc_bitmap = 0;
155     if (FcPatternGetBool(result_pattern, FC_EMBEDDED_BITMAP, 0, &fc_bitmap) ==
156         FcResultMatch) {
157       params_out->use_bitmaps = fc_bitmap;
158     }
159
160     FcBool fc_hinting = 0;
161     if (FcPatternGetBool(result_pattern, FC_HINTING, 0, &fc_hinting) ==
162         FcResultMatch) {
163       int fc_hint_style = FC_HINT_NONE;
164       if (fc_hinting)
165         FcPatternGetInteger(result_pattern, FC_HINT_STYLE, 0, &fc_hint_style);
166       params_out->hinting = ConvertFontconfigHintStyle(fc_hint_style);
167     }
168
169     int fc_rgba = FC_RGBA_NONE;
170     if (FcPatternGetInteger(result_pattern, FC_RGBA, 0, &fc_rgba) ==
171         FcResultMatch)
172       params_out->subpixel_rendering = ConvertFontconfigRgba(fc_rgba);
173   }
174
175   FcPatternDestroy(result_pattern);
176   return true;
177 }
178
179 // Serialize |query| into a string and hash it to a value suitable for use as a
180 // cache key.
181 uint32 HashFontRenderParamsQuery(const FontRenderParamsQuery& query) {
182   return base::Hash(base::StringPrintf("%d|%d|%d|%d|%s",
183       query.for_web_contents, query.pixel_size, query.point_size, query.style,
184       JoinString(query.families, ',').c_str()));
185 }
186
187 }  // namespace
188
189 FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query,
190                                      std::string* family_out) {
191   const uint32 hash = HashFontRenderParamsQuery(query);
192   SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
193
194   {
195     // Try to find a cached result so Fontconfig doesn't need to be queried.
196     base::AutoLock lock(synchronized_cache->lock);
197     Cache::const_iterator it = synchronized_cache->cache.Get(hash);
198     if (it != synchronized_cache->cache.end()) {
199       DVLOG(1) << "Returning cached params for " << hash;
200       const QueryResult& result = it->second;
201       if (family_out)
202         *family_out = result.family;
203       return result.params;
204     }
205   }
206
207   DVLOG(1) << "Computing params for " << hash;
208   if (family_out)
209     family_out->clear();
210
211   // Start with the delegate's settings, but let Fontconfig have the final say.
212   FontRenderParams params;
213   const LinuxFontDelegate* delegate = LinuxFontDelegate::instance();
214   if (delegate)
215     params = delegate->GetDefaultFontRenderParams();
216   QueryFontconfig(query, &params, family_out);
217   if (!params.antialiasing) {
218     // Cairo forces full hinting when antialiasing is disabled, since anything
219     // less than that looks awful; do the same here. Requesting subpixel
220     // rendering or positioning doesn't make sense either.
221     params.hinting = FontRenderParams::HINTING_FULL;
222     params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
223     params.subpixel_positioning = false;
224   } else {
225     // Fontconfig doesn't support configuring subpixel positioning; check a
226     // flag.
227     params.subpixel_positioning =
228         query.for_web_contents ?
229         CommandLine::ForCurrentProcess()->HasSwitch(
230             switches::kEnableWebkitTextSubpixelPositioning) :
231         IsBrowserTextSubpixelPositioningEnabled();
232
233     // To enable subpixel positioning, we need to disable hinting.
234     if (params.subpixel_positioning)
235       params.hinting = FontRenderParams::HINTING_NONE;
236   }
237
238   // Use the first family from the list if Fontconfig didn't suggest a family.
239   if (family_out && family_out->empty() && !query.families.empty())
240     *family_out = query.families[0];
241
242   {
243     // Store the result. It's fine if this overwrites a result that was cached
244     // by a different thread in the meantime; the values should be identical.
245     base::AutoLock lock(synchronized_cache->lock);
246     synchronized_cache->cache.Put(hash,
247         QueryResult(params, family_out ? *family_out : std::string()));
248   }
249
250   return params;
251 }
252
253 void ClearFontRenderParamsCacheForTest() {
254   SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
255   base::AutoLock lock(synchronized_cache->lock);
256   synchronized_cache->cache.Clear();
257 }
258
259 #if defined(OS_CHROMEOS)
260 void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor) {
261   device_scale_factor_for_internal_display = device_scale_factor;
262 }
263 #endif
264
265 }  // namespace gfx