summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog18
-rw-r--r--pango/pangocairo-fcfont.c111
2 files changed, 129 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index fc15b70d..d333d222 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2005-11-04 Federico Mena Quintero <federico@ximian.com>
+
+ Fixes #320665:
+
+ * pango/pangocairo-fcfont.c: Use a simple, fixed-size cache to map
+ gunichars to glyph indices within the font. The cache is
+ described in detail here:
+ http://primates.ximian.com/~federico/news-2005-10.html#gtkfilechooser-profile-5
+ (GlyphCacheEntry): new structure to hold a gunichar and a PangoGlyph.
+ (struct _PangoCairoFcFont): Add a char_to_glyph_cache field.
+ (pango_cairo_fc_font_get_glyph): Allocate the char_to_glyph_cache
+ if needed. We do it on demand because while many fonts will be
+ alive at any one time (in order to cover the whole Unicode space),
+ only very few of them will be actually accessed for glyph lookups.
+ (pango_cairo_fc_font_get_glyph): Look up the gunichar in the
+ cache, and replace the cache entry if necessary.
+ (pango_cairo_fc_font_finalize): Free the char_to_glyph_cache.
+
2005-11-04 Behdad Esfahbod <behdad@gnome.org>
* pango/pangocairo-fontmap.c: Use quarks for GObject data to improve
diff --git a/pango/pangocairo-fcfont.c b/pango/pangocairo-fcfont.c
index eb6825ef..156ff988 100644
--- a/pango/pangocairo-fcfont.c
+++ b/pango/pangocairo-fcfont.c
@@ -44,6 +44,32 @@ typedef struct _PangoCairoFcFont PangoCairoFcFont;
typedef struct _PangoCairoFcFontClass PangoCairoFcFontClass;
typedef struct _PangoCairoFcGlyphInfo PangoCairoFcGlyphInfo;
+#undef PROFILE_GLYPH_CACHE
+#ifdef PROFILE_GLYPH_CACHE
+static long num_cairo_fc_fonts;
+static long max_cairo_fc_fonts;
+static long num_glyph_caches;
+static long max_glyph_caches;
+static long num_glyph_cache_hits;
+static long num_glyph_cache_misses;
+#endif
+
+#define GLYPH_CACHE_NUM_ENTRIES 256
+#define GLYPH_CACHE_MASK 0x000000ff
+
+/* An entry in the fixed-size cache for the gunichar -> glyph mapping. The
+ * cache is indexed by the lower N bits of the gunichar (see
+ * GLYPH_CACHE_NUM_ENTRIES and GLYPH_CACHE_MASK above). For scripts with few
+ * code points, this should provide pretty much instant lookups.
+ *
+ * The "ch" field is zero if that cache entry is invalid.
+ */
+typedef struct
+{
+ gunichar ch;
+ PangoGlyph glyph;
+} GlyphCacheEntry;
+
struct _PangoCairoFcFont
{
PangoFcFont font;
@@ -55,6 +81,8 @@ struct _PangoCairoFcFont
cairo_font_options_t *options;
GHashTable *glyph_info;
+
+ GlyphCacheEntry *char_to_glyph_cache;
};
struct _PangoCairoFcFontClass
@@ -161,7 +189,21 @@ pango_cairo_fc_font_finalize (GObject *object)
g_hash_table_destroy (cffont->glyph_info);
+ if (cffont->char_to_glyph_cache)
+ {
+ g_free (cffont->char_to_glyph_cache);
+#ifdef PROFILE_GLYPH_CACHE
+ num_glyph_caches--;
+ g_assert (num_glyph_caches >= 0);
+#endif
+ }
+
G_OBJECT_CLASS (pango_cairo_fc_font_parent_class)->finalize (object);
+
+#ifdef PROFILE_GLYPH_CACHE
+ num_cairo_fc_fonts--;
+ g_assert (num_cairo_fc_fonts >= 0);
+#endif
}
static void
@@ -313,6 +355,52 @@ pango_cairo_fc_font_unlock_face (PangoFcFont *font)
}
static PangoGlyph
+pango_cairo_fc_font_get_glyph (PangoFcFont *font,
+ gunichar wc)
+{
+ PangoCairoFcFont *cffont = (PangoCairoFcFont *)font;
+ guint idx;
+ GlyphCacheEntry *entry;
+
+ /* We create the cache dynamically because although we'll have a lot of
+ * PangoCairoFcFont structures (one per font in the fontset, as many as needed
+ * to cover the whole Unicode space), we'll only end up using a few of them
+ * for the user's actual text.
+ */
+ if (cffont->char_to_glyph_cache == NULL)
+ {
+ cffont->char_to_glyph_cache = g_new0 (GlyphCacheEntry, GLYPH_CACHE_NUM_ENTRIES);
+#ifdef PROFILE_GLYPH_CACHE
+ num_glyph_caches++;
+ if (num_glyph_caches > max_glyph_caches)
+ max_glyph_caches = num_glyph_caches;
+#endif
+ }
+
+ idx = wc & GLYPH_CACHE_MASK;
+ entry = cffont->char_to_glyph_cache + idx;
+
+ if (entry->ch != wc || wc == 0)
+ {
+#ifdef PROFILE_GLYPH_CACHE
+ num_glyph_cache_misses++;
+/* printf ("cache MISS: cffont %p, gunichar %x = '%c'\n", cffont, wc, (wc < 128) ? wc : 0); */
+#endif
+ entry->ch = wc;
+ entry->glyph = ((PangoFcFontClass *) pango_cairo_fc_font_parent_class)->get_glyph (font, wc);
+ }
+ else
+ {
+#ifdef PROFILE_GLYPH_CACHE
+ num_glyph_cache_hits++;
+/* printf ("cache HIT: cffont %p, gunichar %x = '%c'\n", cffont, wc, (wc < 128) ? wc : 0); */
+#endif
+ }
+
+ return entry->glyph;
+}
+
+static PangoGlyph
pango_cairo_fc_font_real_get_unknown_glyph (PangoFcFont *font,
gunichar wc)
{
@@ -335,6 +423,18 @@ pango_cairo_fc_font_shutdown (PangoFcFont *fcfont)
}
}
+#ifdef PROFILE_GLYPH_CACHE
+static void
+profile_glyph_cache_exit_cb (void)
+{
+ printf ("Maximum number of PangoCairoFcFont objects: %ld\n", max_cairo_fc_fonts);
+ printf ("Maximum number of glyph caches created: %ld (%ld KB)\n", max_glyph_caches, (max_glyph_caches * sizeof (GlyphCacheEntry) * GLYPH_CACHE_NUM_ENTRIES) / 1024);
+ printf ("Glyph cache hits: %ld\n", num_glyph_cache_hits);
+ printf ("Glyph cache misses: %ld\n", num_glyph_cache_misses);
+ printf ("Glyph cache miss rate: %f%%\n", 100.0 * (double) num_glyph_cache_misses / (num_glyph_cache_misses + num_glyph_cache_hits));
+}
+#endif
+
static void
pango_cairo_fc_font_class_init (PangoCairoFcFontClass *class)
{
@@ -349,8 +449,13 @@ pango_cairo_fc_font_class_init (PangoCairoFcFontClass *class)
fc_font_class->lock_face = pango_cairo_fc_font_lock_face;
fc_font_class->unlock_face = pango_cairo_fc_font_unlock_face;
+ fc_font_class->get_glyph = pango_cairo_fc_font_get_glyph;
fc_font_class->get_unknown_glyph = pango_cairo_fc_font_real_get_unknown_glyph;
fc_font_class->shutdown = pango_cairo_fc_font_shutdown;
+
+#ifdef PROFILE_GLYPH_CACHE
+ atexit (profile_glyph_cache_exit_cb);
+#endif
}
static void
@@ -360,6 +465,12 @@ pango_cairo_fc_font_init (PangoCairoFcFont *cffont)
NULL,
NULL,
(GDestroyNotify)g_free);
+
+#ifdef PROFILE_GLYPH_CACHE
+ num_cairo_fc_fonts++;
+ if (num_cairo_fc_fonts > max_cairo_fc_fonts)
+ max_cairo_fc_fonts = num_cairo_fc_fonts;
+#endif
}
/********************