diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-14 17:19:29 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-14 17:19:29 +0000 |
commit | f5c655de4121427b4d9d9dc218efd29afa8bba8c (patch) | |
tree | d731b3ec3b974c96d21c54610b529c474a0fd7b2 /app/gfx/font_gtk.cc | |
parent | 2e360844a496013fc57c527fd5d6c4da165b09f7 (diff) | |
download | chromium_src-f5c655de4121427b4d9d9dc218efd29afa8bba8c.zip chromium_src-f5c655de4121427b4d9d9dc218efd29afa8bba8c.tar.gz chromium_src-f5c655de4121427b4d9d9dc218efd29afa8bba8c.tar.bz2 |
Attempt 2 at converting font from Skia to Pango. The slow down appears to be entirely attributed to asking for the metrics. To get back the performance I did the following:
. Only ask for the metrics when needed. We appear to create 6 or so fonts very early on without needing the metrics. This ensures we only take the metrics load hit when needed.
. Keep a cache of the metrics around. This ensures that we only load the metrics once.
I didn't dig through the Pango source to see why Pango is slow at getting metrics. I suspect it's the first person to ask for metrics incurs a penalty as Pango accesses the disk. The code we have now is the same as that as Gtk.
Review URL: http://codereview.chromium.org/199101
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26116 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app/gfx/font_gtk.cc')
-rw-r--r-- | app/gfx/font_gtk.cc | 248 |
1 files changed, 184 insertions, 64 deletions
diff --git a/app/gfx/font_gtk.cc b/app/gfx/font_gtk.cc index 244810a..897e8f0 100644 --- a/app/gfx/font_gtk.cc +++ b/app/gfx/font_gtk.cc @@ -7,56 +7,87 @@ #include <fontconfig/fontconfig.h> #include <gtk/gtk.h> +#include "app/gfx/canvas.h" #include "base/string_util.h" namespace gfx { -Font* Font::default_font_ = NULL; +namespace { + +// Returns a PangoContext that is used to get metrics. The returned context +// should never be freed. +PangoContext* get_context() { + static PangoContext* context = NULL; + if (!context) { + context = gdk_pango_context_get_for_screen(gdk_screen_get_default()); + pango_context_set_language(context, gtk_get_default_language()); + } + return context; +} -// Find the best match font for |family_name| in the same way as Skia -// to make sure CreateFont() successfully creates a default font. In -// Skia, it only checks the best match font. If it failed to find -// one, SkTypeface will be NULL for that font family. It eventually -// causes a segfault. For example, family_name = "Sans" and system -// may have various fonts. The first font family in FcPattern will be -// "DejaVu Sans" but a font family returned by FcFontMatch will be "VL -// PGothic". In this case, SkTypeface for "Sans" returns NULL even if -// the system has a font for "Sans" font family. See FontMatch() in -// skia/ports/SkFontHost_fontconfig.cpp for more detail. -static std::wstring FindBestMatchFontFamilyName(const char* family_name) { - FcPattern* pattern = FcPatternCreate(); - FcValue fcvalue; - fcvalue.type = FcTypeString; - char* family_name_copy = strdup(family_name); - fcvalue.u.s = reinterpret_cast<FcChar8*>(family_name_copy); - FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0); - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - FcResult result; - FcPattern* match = FcFontMatch(0, pattern, &result); - DCHECK(match) << "Could not find font: " << family_name; - FcChar8* match_family; - FcPatternGetString(match, FC_FAMILY, 0, &match_family); - - std::wstring font_family = UTF8ToWide( - reinterpret_cast<char*>(match_family)); - FcPatternDestroy(match); - FcPatternDestroy(pattern); - free(family_name_copy); - return font_family; } +Font* Font::default_font_ = NULL; + // static Font Font::CreateFont(PangoFontDescription* desc) { - gint size = pango_font_description_get_size(desc); - const char* family_name = pango_font_description_get_family(desc); + return Font(desc); +} - // Find best match font for |family_name| to make sure we can get - // a SkTypeface for the default font. - // TODO(agl): remove this. - std::wstring font_family = FindBestMatchFontFamilyName(family_name); +Font Font::DeriveFont(int size_delta, int style) const { + // If the delta is negative, if must not push the size below 1 + if (size_delta < 0) + DCHECK_LT(-size_delta, font_ref_->size()); + + PangoFontDescription* pfd = pango_font_description_copy(nativeFont()); + pango_font_description_set_size( + pfd, (font_ref_->size() + size_delta) * PANGO_SCALE); + pango_font_description_set_style( + pfd, style & ITALIC ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); + pango_font_description_set_weight( + pfd, style & BOLD ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + Font font(pfd); + pango_font_description_free(pfd); + return font; +} + +int Font::height() const { + return font_ref_->GetHeight(); +} - return Font(CreateFont(font_family, size / PANGO_SCALE)); +int Font::baseline() const { + return font_ref_->GetAscent(); +} + +int Font::ave_char_width() const { + return font_ref_->GetAveCharWidth(); +} + +int Font::GetStringWidth(const std::wstring& text) const { + int width = 0, height = 0; + + Canvas::SizeStringInt(text, *this, &width, &height, 0); + return width; +} + +int Font::GetExpectedTextWidth(int length) const { + return length * font_ref_->GetAveCharWidth(); +} + +int Font::style() const { + return font_ref_->style(); +} + +std::wstring Font::FontName() { + return font_ref_->family(); +} + +int Font::FontSize() { + return font_ref_->size(); +} + +PangoFontDescription* Font::nativeFont() const { + return font_ref_->pfd(); } // Get the default gtk system font (name and size). @@ -75,41 +106,130 @@ Font::Font() { PangoFontDescription* desc = pango_font_description_from_string(font_name); - default_font_ = new Font(CreateFont(desc)); + default_font_ = new Font(desc); pango_font_description_free(desc); g_free(font_name); - - DCHECK(default_font_); } - CopyFont(*default_font_); + *this = *default_font_; } // static -PangoFontDescription* Font::PangoFontFromGfxFont( - const gfx::Font& gfx_font) { - gfx::Font font = gfx_font; // Copy so we can call non-const methods. +Font Font::CreateFont(const std::wstring& font_family, int font_size) { + DCHECK_GT(font_size, 0); + PangoFontDescription* pfd = pango_font_description_new(); - pango_font_description_set_family(pfd, WideToUTF8(font.FontName()).c_str()); - pango_font_description_set_size(pfd, font.FontSize() * PANGO_SCALE); - - switch (font.style()) { - case gfx::Font::NORMAL: - // Nothing to do, should already be PANGO_STYLE_NORMAL. - break; - case gfx::Font::BOLD: - pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD); - break; - case gfx::Font::ITALIC: - pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC); - break; - case gfx::Font::UNDERLINED: - // TODO(deanm): How to do underlined? Where do we use it? Probably have - // to paint it ourselves, see pango_font_metrics_get_underline_position. - break; + pango_font_description_set_family(pfd, WideToUTF8(font_family).c_str()); + pango_font_description_set_size(pfd, font_size * PANGO_SCALE); + pango_font_description_set_style(pfd, PANGO_STYLE_NORMAL); + pango_font_description_set_weight(pfd, PANGO_WEIGHT_NORMAL); + Font font(pfd); + pango_font_description_free(pfd); + return font; +} + +Font::Font(PangoFontDescription* desc) { + PangoContext* context = get_context(); + pango_context_set_font_description(context, desc); + int size = pango_font_description_get_size(desc) / PANGO_SCALE; + int style = 0; + if (pango_font_description_get_weight(desc) >= PANGO_WEIGHT_BOLD) + style |= BOLD; + if (pango_font_description_get_style(desc) == PANGO_STYLE_ITALIC) + style |= ITALIC; + std::wstring name(UTF8ToWide(pango_font_description_get_family(desc))); + font_ref_ = PangoFontRef::Create(desc, name, size, style); +} + +// PangoFontRef --------------------------------------------------------------- + +// Maximum number of PangoFontRefs cached. +static const size_t kMaxCacheSize = 20; + +// static +Font::PangoFontRef* Font::PangoFontRef::Create(PangoFontDescription* pfd, + const std::wstring& family, + int size, + int style) { + Cache* cache = GetCache(); + for (Cache::iterator i = cache->begin(); i != cache->end(); ++i) { + PangoFontRef* ref = *i; + if (ref->size() == size && ref->style() == style && + ref->family() == family) + return ref; } - return pfd; + PangoFontRef* ref = new PangoFontRef(pfd, family, size, style); + AddToCache(ref); + return ref; +} + +Font::PangoFontRef::~PangoFontRef() { + pango_font_description_free(pfd_); +} + +int Font::PangoFontRef::GetHeight() const { + CalculateMetricsIfNecessary(); + return height_; +} + +int Font::PangoFontRef::GetAscent() const { + CalculateMetricsIfNecessary(); + return ascent_; +} + +int Font::PangoFontRef::GetAveCharWidth() const { + CalculateMetricsIfNecessary(); + return ave_char_width_; +} + +Font::PangoFontRef::PangoFontRef(PangoFontDescription* pfd, + const std::wstring& family, + int size, + int style) + : pfd_(pango_font_description_copy(pfd)), + family_(family), + size_(size), + style_(style), + height_(0), + ascent_(0), + ave_char_width_(0), + calculated_metrics_(false) { +} + +void Font::PangoFontRef::CalculateMetricsIfNecessary() const { + if (calculated_metrics_) + return; + + calculated_metrics_ = true; + PangoContext* context = get_context(); + pango_context_set_font_description(context, pfd_); + PangoFontMetrics* metrics = pango_context_get_metrics(context, pfd_, NULL); + ascent_ = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; + height_ = ascent_ + pango_font_metrics_get_descent(metrics) / PANGO_SCALE; + ave_char_width_ = pango_font_metrics_get_approximate_char_width(metrics) / + PANGO_SCALE; + pango_font_metrics_unref(metrics); +} + +// static +Font::PangoFontRef::Cache* Font::PangoFontRef::GetCache() { + static Cache* cache = NULL; + if (!cache) + cache = new Cache(); + return cache; +} + +// static +void Font::PangoFontRef::AddToCache(PangoFontRef* ref) { + ref->AddRef(); + Cache* cache = GetCache(); + cache->push_back(ref); + while (cache->size() >= kMaxCacheSize) { + PangoFontRef* ref = cache->front(); + cache->pop_front(); + ref->Release(); + } } } // namespace gfx |