// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/gfx/font_render_params_linux.h"
+#include "ui/gfx/font_render_params.h"
+
+#include <fontconfig/fontconfig.h>
#include "base/command_line.h"
+#include "base/containers/mru_cache.h"
+#include "base/hash.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/linux_font_delegate.h"
#include "ui/gfx/switches.h"
-#if defined(TOOLKIT_GTK)
-#include <gtk/gtk.h>
-#else
-#include <fontconfig/fontconfig.h>
-#endif
-
namespace gfx {
namespace {
-bool SubpixelPositioningRequested(bool renderer) {
- return CommandLine::ForCurrentProcess()->HasSwitch(
- renderer ?
- switches::kEnableWebkitTextSubpixelPositioning :
- switches::kEnableBrowserTextSubpixelPositioning);
+#if defined(OS_CHROMEOS)
+// A device scale factor for an internal display (if any)
+// that is used to determine if subpixel positioning should be used.
+float device_scale_factor_for_internal_display = 1.0f;
+#endif
+
+// Keyed by hashes of FontRenderParamQuery structs from
+// HashFontRenderParamsQuery().
+typedef base::MRUCache<uint32, FontRenderParams> Cache;
+
+// Number of recent GetFontRenderParams() results to cache.
+const size_t kCacheSize = 20;
+
+// A cache and the lock that must be held while accessing it.
+// GetFontRenderParams() is called by both the UI thread and the sandbox IPC
+// thread.
+struct SynchronizedCache {
+ SynchronizedCache() : cache(kCacheSize) {}
+
+ base::Lock lock;
+ Cache cache;
+};
+
+base::LazyInstance<SynchronizedCache>::Leaky g_synchronized_cache =
+ LAZY_INSTANCE_INITIALIZER;
+
+bool IsBrowserTextSubpixelPositioningEnabled() {
+#if defined(OS_CHROMEOS)
+ return device_scale_factor_for_internal_display > 1.0f;
+#else
+ return false;
+#endif
}
-// Initializes |params| with the system's default settings. |renderer| is true
-// when setting WebKit renderer defaults.
-void LoadDefaults(FontRenderParams* params, bool renderer) {
-#if defined(TOOLKIT_GTK)
- params->antialiasing = true;
- // TODO(wangxianzhu): autohinter is now true to keep original behavior
- // of WebKit, but it might not be the best value.
- params->autohinter = true;
- params->use_bitmaps = true;
- params->hinting = FontRenderParams::HINTING_SLIGHT;
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
-
- GtkSettings* gtk_settings = gtk_settings_get_default();
- CHECK(gtk_settings);
- gint gtk_antialias = 0;
- gint gtk_hinting = 0;
- gchar* gtk_hint_style = NULL;
- gchar* gtk_rgba = NULL;
- g_object_get(gtk_settings,
- "gtk-xft-antialias", >k_antialias,
- "gtk-xft-hinting", >k_hinting,
- "gtk-xft-hintstyle", >k_hint_style,
- "gtk-xft-rgba", >k_rgba,
- NULL);
-
- // g_object_get() doesn't tell us whether the properties were present or not,
- // but if they aren't (because gnome-settings-daemon isn't running), we'll get
- // NULL values for the strings.
- if (gtk_hint_style && gtk_rgba) {
- params->antialiasing = gtk_antialias;
-
- if (gtk_hinting == 0 || strcmp(gtk_hint_style, "hintnone") == 0)
- params->hinting = FontRenderParams::HINTING_NONE;
- else if (strcmp(gtk_hint_style, "hintslight") == 0)
- params->hinting = FontRenderParams::HINTING_SLIGHT;
- else if (strcmp(gtk_hint_style, "hintmedium") == 0)
- params->hinting = FontRenderParams::HINTING_MEDIUM;
- else if (strcmp(gtk_hint_style, "hintfull") == 0)
- params->hinting = FontRenderParams::HINTING_FULL;
-
- if (strcmp(gtk_rgba, "none") == 0)
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
- else if (strcmp(gtk_rgba, "rgb") == 0)
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
- else if (strcmp(gtk_rgba, "bgr") == 0)
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_BGR;
- else if (strcmp(gtk_rgba, "vrgb") == 0)
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_VRGB;
- else if (strcmp(gtk_rgba, "vbgr") == 0)
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_VBGR;
+// Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
+FontRenderParams::Hinting ConvertFontconfigHintStyle(int hint_style) {
+ switch (hint_style) {
+ case FC_HINT_SLIGHT: return FontRenderParams::HINTING_SLIGHT;
+ case FC_HINT_MEDIUM: return FontRenderParams::HINTING_MEDIUM;
+ case FC_HINT_FULL: return FontRenderParams::HINTING_FULL;
+ default: return FontRenderParams::HINTING_NONE;
}
+}
- g_free(gtk_hint_style);
- g_free(gtk_rgba);
-#else
- // For non-GTK builds (read: Aura), just use reasonable hardcoded values.
- params->antialiasing = true;
- params->autohinter = true;
- params->use_bitmaps = true;
- params->hinting = FontRenderParams::HINTING_SLIGHT;
+// Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
+FontRenderParams::SubpixelRendering ConvertFontconfigRgba(int rgba) {
+ switch (rgba) {
+ case FC_RGBA_RGB: return FontRenderParams::SUBPIXEL_RENDERING_RGB;
+ case FC_RGBA_BGR: return FontRenderParams::SUBPIXEL_RENDERING_BGR;
+ case FC_RGBA_VRGB: return FontRenderParams::SUBPIXEL_RENDERING_VRGB;
+ case FC_RGBA_VBGR: return FontRenderParams::SUBPIXEL_RENDERING_VBGR;
+ default: return FontRenderParams::SUBPIXEL_RENDERING_NONE;
+ }
+}
- // Fetch default subpixel rendering settings from FontConfig.
+// Queries Fontconfig for rendering settings and updates |params_out| and
+// |family_out| (if non-NULL). Returns false on failure.
+bool QueryFontconfig(const FontRenderParamsQuery& query,
+ FontRenderParams* params_out,
+ std::string* family_out) {
FcPattern* pattern = FcPatternCreate();
+ CHECK(pattern);
+
+ FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+
+ for (std::vector<std::string>::const_iterator it = query.families.begin();
+ it != query.families.end(); ++it) {
+ FcPatternAddString(
+ pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(it->c_str()));
+ }
+ if (query.pixel_size > 0)
+ FcPatternAddDouble(pattern, FC_PIXEL_SIZE, query.pixel_size);
+ if (query.point_size > 0)
+ FcPatternAddInteger(pattern, FC_SIZE, query.point_size);
+ if (query.style >= 0) {
+ FcPatternAddInteger(pattern, FC_SLANT,
+ (query.style & Font::ITALIC) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
+ FcPatternAddInteger(pattern, FC_WEIGHT,
+ (query.style & Font::BOLD) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL);
+ }
+
FcConfigSubstitute(NULL, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
- FcPattern* match = FcFontMatch(0, pattern, &result);
- DCHECK(match);
- int fc_rgba = FC_RGBA_RGB;
- FcPatternGetInteger(match, FC_RGBA, 0, &fc_rgba);
+ FcPattern* match = FcFontMatch(NULL, pattern, &result);
FcPatternDestroy(pattern);
- FcPatternDestroy(match);
+ if (!match)
+ return false;
+
+ if (family_out) {
+ FcChar8* family = NULL;
+ FcPatternGetString(match, FC_FAMILY, 0, &family);
+ if (family)
+ family_out->assign(reinterpret_cast<const char*>(family));
+ }
- switch (fc_rgba) {
- case FC_RGBA_RGB:
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
- break;
- case FC_RGBA_BGR:
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_BGR;
- break;
- case FC_RGBA_VRGB:
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_VRGB;
- break;
- case FC_RGBA_VBGR:
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_VBGR;
- break;
- default:
- params->subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
+ if (params_out) {
+ FcBool fc_antialias = 0;
+ if (FcPatternGetBool(match, FC_ANTIALIAS, 0, &fc_antialias) ==
+ FcResultMatch) {
+ params_out->antialiasing = fc_antialias;
+ }
+
+ FcBool fc_autohint = 0;
+ if (FcPatternGetBool(match, FC_AUTOHINT, 0, &fc_autohint) ==
+ FcResultMatch) {
+ params_out->autohinter = fc_autohint;
+ }
+
+ FcBool fc_bitmap = 0;
+ if (FcPatternGetBool(match, FC_EMBEDDED_BITMAP, 0, &fc_bitmap) ==
+ FcResultMatch) {
+ params_out->use_bitmaps = fc_bitmap;
+ }
+
+ FcBool fc_hinting = 0;
+ if (FcPatternGetBool(match, FC_HINTING, 0, &fc_hinting) == FcResultMatch) {
+ int fc_hint_style = FC_HINT_NONE;
+ if (fc_hinting)
+ FcPatternGetInteger(match, FC_HINT_STYLE, 0, &fc_hint_style);
+ params_out->hinting = ConvertFontconfigHintStyle(fc_hint_style);
+ }
+
+ int fc_rgba = FC_RGBA_NONE;
+ if (FcPatternGetInteger(match, FC_RGBA, 0, &fc_rgba) == FcResultMatch)
+ params_out->subpixel_rendering = ConvertFontconfigRgba(fc_rgba);
}
-#endif
- params->subpixel_positioning = SubpixelPositioningRequested(renderer);
+ FcPatternDestroy(match);
+ return true;
+}
- // To enable subpixel positioning, we need to disable hinting.
- if (params->subpixel_positioning)
- params->hinting = FontRenderParams::HINTING_NONE;
+// Serialize |query| into a string and hash it to a value suitable for use as a
+// cache key.
+uint32 HashFontRenderParamsQuery(const FontRenderParamsQuery& query) {
+ return base::Hash(base::StringPrintf("%d|%d|%d|%d|%s",
+ query.for_web_contents, query.pixel_size, query.point_size, query.style,
+ JoinString(query.families, ',').c_str()));
}
} // namespace
-const FontRenderParams& GetDefaultFontRenderParams() {
- static bool loaded_defaults = false;
- static FontRenderParams default_params;
- if (!loaded_defaults)
- LoadDefaults(&default_params, /* renderer */ false);
- loaded_defaults = true;
- return default_params;
+FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query,
+ std::string* family_out) {
+ const uint32 hash = HashFontRenderParamsQuery(query);
+ if (!family_out) {
+ // The family returned by Fontconfig isn't part of FontRenderParams, so we
+ // can only return a value from the cache if it wasn't requested.
+ SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
+ base::AutoLock lock(synchronized_cache->lock);
+ Cache::const_iterator it = synchronized_cache->cache.Get(hash);
+ if (it != synchronized_cache->cache.end()) {
+ DVLOG(1) << "Returning cached params for " << hash;
+ return it->second;
+ }
+ } else {
+ family_out->clear();
+ }
+ DVLOG(1) << "Computing params for " << hash
+ << (family_out ? " (family requested)" : "");
+
+ // Start with the delegate's settings, but let Fontconfig have the final say.
+ FontRenderParams params;
+ const LinuxFontDelegate* delegate = LinuxFontDelegate::instance();
+ if (delegate)
+ params = delegate->GetDefaultFontRenderParams();
+ QueryFontconfig(query, ¶ms, family_out);
+ if (!params.antialiasing) {
+ // Cairo forces full hinting when antialiasing is disabled, since anything
+ // less than that looks awful; do the same here. Requesting subpixel
+ // rendering or positioning doesn't make sense either.
+ params.hinting = FontRenderParams::HINTING_FULL;
+ params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE;
+ params.subpixel_positioning = false;
+ } else {
+ // Fontconfig doesn't support configuring subpixel positioning; check a
+ // flag.
+ params.subpixel_positioning =
+ query.for_web_contents ?
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableWebkitTextSubpixelPositioning) :
+ IsBrowserTextSubpixelPositioningEnabled();
+
+ // To enable subpixel positioning, we need to disable hinting.
+ if (params.subpixel_positioning)
+ params.hinting = FontRenderParams::HINTING_NONE;
+ }
+
+ // Use the first family from the list if Fontconfig didn't suggest a family.
+ if (family_out && family_out->empty() && !query.families.empty())
+ *family_out = query.families[0];
+
+ // Store the computed struct. It's fine if this overwrites a struct that was
+ // cached by a different thread in the meantime; the values should be
+ // identical.
+ SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
+ base::AutoLock lock(synchronized_cache->lock);
+ synchronized_cache->cache.Put(hash, params);
+
+ return params;
}
-const FontRenderParams& GetDefaultWebKitFontRenderParams() {
- static bool loaded_defaults = false;
- static FontRenderParams default_params;
- if (!loaded_defaults)
- LoadDefaults(&default_params, /* renderer */ true);
- loaded_defaults = true;
- return default_params;
+void ClearFontRenderParamsCacheForTest() {
+ SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
+ base::AutoLock lock(synchronized_cache->lock);
+ synchronized_cache->cache.Clear();
}
-bool GetDefaultWebkitSubpixelPositioning() {
- return SubpixelPositioningRequested(true);
+#if defined(OS_CHROMEOS)
+void SetFontRenderParamsDeviceScaleFactor(float device_scale_factor) {
+ device_scale_factor_for_internal_display = device_scale_factor;
}
+#endif
} // namespace gfx