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 { 43 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 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. 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. 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. 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. 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 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, ¶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; 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 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) 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