Home | History | Annotate | Download | only in gfx
      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, &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 
    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