summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-14 17:19:29 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-14 17:19:29 +0000
commitf5c655de4121427b4d9d9dc218efd29afa8bba8c (patch)
treed731b3ec3b974c96d21c54610b529c474a0fd7b2 /app
parent2e360844a496013fc57c527fd5d6c4da165b09f7 (diff)
downloadchromium_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')
-rw-r--r--app/app.gyp1
-rw-r--r--app/gfx/canvas_linux.cc4
-rw-r--r--app/gfx/font.h111
-rw-r--r--app/gfx/font_gtk.cc248
-rw-r--r--app/gfx/font_skia.cc160
-rw-r--r--app/gfx/font_unittest.cc7
6 files changed, 261 insertions, 270 deletions
diff --git a/app/app.gyp b/app/app.gyp
index e58b762..b610b02 100644
--- a/app/app.gyp
+++ b/app/app.gyp
@@ -76,7 +76,6 @@
'gfx/font.h',
'gfx/font_gtk.cc',
'gfx/font_mac.mm',
- 'gfx/font_skia.cc',
'gfx/font_win.cc',
'gfx/color_utils.cc',
'gfx/color_utils.h',
diff --git a/app/gfx/canvas_linux.cc b/app/gfx/canvas_linux.cc
index 7ed1904..1614f44 100644
--- a/app/gfx/canvas_linux.cc
+++ b/app/gfx/canvas_linux.cc
@@ -130,9 +130,7 @@ static void SetupPangoLayout(PangoLayout* layout,
PANGO_WRAP_WORD_CHAR : PANGO_WRAP_WORD);
}
- PangoFontDescription* desc = gfx::Font::PangoFontFromGfxFont(font);
- pango_layout_set_font_description(layout, desc);
- pango_font_description_free(desc);
+ pango_layout_set_font_description(layout, font.nativeFont());
}
// static
diff --git a/app/gfx/font.h b/app/gfx/font.h
index e53bd79..60e94d2 100644
--- a/app/gfx/font.h
+++ b/app/gfx/font.h
@@ -9,12 +9,12 @@
#include <string>
+#if defined(OS_LINUX)
+#include <list>
+#endif
+
#if defined(OS_WIN)
typedef struct HFONT__* HFONT;
-#elif defined(OS_LINUX)
-#include "third_party/skia/include/core/SkRefCnt.h"
-class SkPaint;
-class SkTypeface;
#endif
#if defined(OS_WIN)
@@ -28,8 +28,7 @@ class NSFont;
typedef NSFont* NativeFont;
#elif defined(OS_LINUX)
typedef struct _PangoFontDescription PangoFontDescription;
-class SkTypeface;
-typedef SkTypeface* NativeFont;
+typedef PangoFontDescription* NativeFont;
#else // null port.
#error No known OS defined
#endif
@@ -102,6 +101,10 @@ class Font {
// Font Size.
int FontSize();
+ // Returns a handle to the native font.
+ // NOTE: on linux this returns the PangoFontDescription* being held by this
+ // object. You should not modify or free it. If you need to use it, make a
+ // copy of it by way of pango_font_description_copy(nativeFont()).
NativeFont nativeFont() const;
// Creates a font with the default name and style.
@@ -126,16 +129,6 @@ class Font {
}
#elif defined(OS_LINUX)
static Font CreateFont(PangoFontDescription* desc);
- // We need a copy constructor and assignment operator to deal with
- // the Skia reference counting.
- Font(const Font& other);
- Font& operator=(const Font& other);
- // Setup a Skia context to use the current typeface
- void PaintSetup(SkPaint* paint) const;
-
- // Converts |gfx_font| to a new pango font. Free the returned font with
- // pango_font_description_free().
- static PangoFontDescription* PangoFontFromGfxFont(const gfx::Font& gfx_font);
#endif
private:
@@ -196,32 +189,74 @@ class Font {
// Indirect reference to the HFontRef, which references the underlying HFONT.
scoped_refptr<HFontRef> font_ref_;
#elif defined(OS_LINUX)
- explicit Font(SkTypeface* typeface, const std::wstring& name,
- int size, int style);
- // Calculate and cache the font metrics.
- void calculateMetrics();
- // Make |this| a copy of |other|.
- void CopyFont(const Font& other);
+ // Used internally on Linux to cache information about the font. Each
+ // Font maintains a reference to a PangoFontRef. Coping a Font ups the
+ // refcount of the corresponding PangoFontRef.
+ //
+ // As obtaining metrics (height, ascent, average char width) is expensive,
+ // the metrics are only obtained as needed. Additionally PangoFontRef
+ // maintains a static cache of PangoFontRefs. When a PangoFontRef is needed
+ // the cache is checked first.
+ class PangoFontRef : public base::RefCounted<PangoFontRef> {
+ public:
+ ~PangoFontRef();
+
+ // Creates or returns a cached PangoFontRef of the given family, size and
+ // style.
+ static PangoFontRef* Create(PangoFontDescription* pfd,
+ const std::wstring& family,
+ int size,
+ int style);
+
+ PangoFontDescription* pfd() const { return pfd_; }
+ const std::wstring& family() const { return family_; }
+ int size() const { return size_; }
+ int style() const { return style_; }
+ int GetHeight() const;
+ int GetAscent() const;
+ int GetAveCharWidth() const;
- // The default font, used for the default constructor.
- static Font* default_font_;
+ private:
+ typedef std::list<PangoFontRef*> Cache;
- // These two both point to the same SkTypeface. We use the SkAutoUnref to
- // handle the reference counting, but without @typeface_ we would have to
- // cast the SkRefCnt from @typeface_helper_ every time.
- scoped_ptr<SkAutoUnref> typeface_helper_;
- SkTypeface *typeface_;
+ PangoFontRef(PangoFontDescription* pfd,
+ const std::wstring& family,
+ int size,
+ int style);
- // Additional information about the face
- // Skia actually expects a family name and not a font name.
- std::wstring font_family_;
- int font_size_;
- int style_;
+ void CalculateMetricsIfNecessary() const;
- // Cached metrics, generated at construction
- int height_;
- int ascent_;
- int avg_width_;
+ // Returns the cache.
+ static Cache* GetCache();
+
+ // Adds |ref| to the cache, removing an existing PangoFontRef if there are
+ // too many.
+ static void AddToCache(PangoFontRef* ref);
+
+ PangoFontDescription* pfd_;
+ const std::wstring family_;
+ const int size_;
+ const int style_;
+
+ // Metrics related members. As these are expensive to calculate they are
+ // calculated the first time requested.
+ mutable int height_;
+ mutable int ascent_;
+ mutable int ave_char_width_;
+
+ // Have the metrics related members been determined yet (height_, ascent_
+ // ave_char_width_)?
+ mutable bool calculated_metrics_;
+
+ DISALLOW_COPY_AND_ASSIGN(PangoFontRef);
+ };
+
+ explicit Font(PangoFontDescription* pfd);
+
+ // The default font, used for the default constructor.
+ static Font* default_font_;
+
+ scoped_refptr<PangoFontRef> font_ref_;
#elif defined(OS_MACOSX)
explicit Font(const std::wstring& font_name, int font_size, int style);
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
diff --git a/app/gfx/font_skia.cc b/app/gfx/font_skia.cc
deleted file mode 100644
index 4308bf6..0000000
--- a/app/gfx/font_skia.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "app/gfx/font.h"
-
-#include "app/gfx/canvas.h"
-
-#include "base/logging.h"
-#include "base/sys_string_conversions.h"
-
-#include "third_party/skia/include/core/SkTypeface.h"
-#include "third_party/skia/include/core/SkPaint.h"
-
-namespace gfx {
-
-Font::Font(const Font& other) {
- CopyFont(other);
-}
-
-Font& Font::operator=(const Font& other) {
- CopyFont(other);
- return *this;
-}
-
-Font::Font(SkTypeface* tf, const std::wstring& font_family, int font_size,
- int style)
- : typeface_helper_(new SkAutoUnref(tf)),
- typeface_(tf),
- font_family_(font_family),
- font_size_(font_size),
- style_(style) {
- tf->ref();
- calculateMetrics();
-}
-
-void Font::calculateMetrics() {
- SkPaint paint;
- SkPaint::FontMetrics metrics;
-
- PaintSetup(&paint);
- paint.getFontMetrics(&metrics);
-
- ascent_ = SkScalarRound(-metrics.fAscent);
- height_ = SkScalarRound(-metrics.fAscent + metrics.fDescent +
- metrics.fLeading);
-
- if (metrics.fAvgCharWidth) {
- avg_width_ = SkScalarRound(metrics.fAvgCharWidth);
- } else {
- static const char x_char = 'x';
- paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
- SkScalar width = paint.measureText(&x_char, 1);
-
- avg_width_ = static_cast<int>(ceilf(SkScalarToFloat(width)));
- }
-}
-
-void Font::CopyFont(const Font& other) {
- typeface_helper_.reset(new SkAutoUnref(other.typeface_));
- typeface_ = other.typeface_;
- typeface_->ref();
- font_family_ = other.font_family_;
- font_size_ = other.font_size_;
- style_ = other.style_;
- height_ = other.height_;
- ascent_ = other.ascent_;
- avg_width_ = other.avg_width_;
-}
-
-int Font::height() const {
- return height_;
-}
-
-int Font::baseline() const {
- return ascent_;
-}
-
-int Font::ave_char_width() const {
- return avg_width_;
-}
-
-Font Font::CreateFont(const std::wstring& font_family, int font_size) {
- DCHECK_GT(font_size, 0);
-
- SkTypeface* tf = SkTypeface::CreateFromName(
- base::SysWideToUTF8(font_family).c_str(), SkTypeface::kNormal);
- // Temporary CHECK for tracking down
- // http://code.google.com/p/chromium/issues/detail?id=12530
- CHECK(tf) << "Could not find font: " << base::SysWideToUTF8(font_family);
- SkAutoUnref tf_helper(tf);
-
- return Font(tf, font_family, font_size, NORMAL);
-}
-
-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_size_);
- }
-
- if (style == style_) {
- // Fast path, we just use the same typeface at a different size
- return Font(typeface_, font_family_, font_size_ + size_delta, style_);
- }
-
- // If the style has changed we may need to load a new face
- int skstyle = SkTypeface::kNormal;
- if (BOLD & style)
- skstyle |= SkTypeface::kBold;
- if (ITALIC & style)
- skstyle |= SkTypeface::kItalic;
-
- SkTypeface* tf = SkTypeface::CreateFromName(
- base::SysWideToUTF8(font_family_).c_str(),
- static_cast<SkTypeface::Style>(skstyle));
- SkAutoUnref tf_helper(tf);
-
- return Font(tf, font_family_, font_size_ + size_delta, skstyle);
-}
-
-void Font::PaintSetup(SkPaint* paint) const {
- paint->setAntiAlias(false);
- paint->setSubpixelText(false);
- paint->setTextSize(SkFloatToScalar(font_size_));
- paint->setTypeface(typeface_);
- paint->setFakeBoldText((BOLD & style_) && !typeface_->isBold());
- paint->setTextSkewX((ITALIC & style_) && !typeface_->isItalic() ?
- -SK_Scalar1/4 : 0);
-}
-
-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 * avg_width_;
-}
-
-
-int Font::style() const {
- return style_;
-}
-
-std::wstring Font::FontName() {
- return font_family_;
-}
-
-int Font::FontSize() {
- return font_size_;
-}
-
-NativeFont Font::nativeFont() const {
- return typeface_;
-}
-
-} // namespace gfx
diff --git a/app/gfx/font_unittest.cc b/app/gfx/font_unittest.cc
index dc81ca8..d0f4fff 100644
--- a/app/gfx/font_unittest.cc
+++ b/app/gfx/font_unittest.cc
@@ -10,8 +10,7 @@ namespace {
using gfx::Font;
-class FontTest : public testing::Test {
-};
+typedef testing::Test FontTest;
TEST_F(FontTest, LoadArial) {
Font cf(Font::CreateFont(L"Arial", 16));
@@ -31,13 +30,13 @@ TEST_F(FontTest, LoadArialBold) {
TEST_F(FontTest, Ascent) {
Font cf(Font::CreateFont(L"Arial", 16));
ASSERT_GT(cf.baseline(), 2);
- ASSERT_LT(cf.baseline(), 20);
+ ASSERT_LT(cf.baseline(), 22);
}
TEST_F(FontTest, Height) {
Font cf(Font::CreateFont(L"Arial", 16));
ASSERT_GT(cf.baseline(), 2);
- ASSERT_LT(cf.baseline(), 20);
+ ASSERT_LT(cf.baseline(), 22);
}
TEST_F(FontTest, AvgWidths) {