summaryrefslogtreecommitdiff
path: root/pango/pangoft2-fontcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango/pangoft2-fontcache.c')
-rw-r--r--pango/pangoft2-fontcache.c305
1 files changed, 305 insertions, 0 deletions
diff --git a/pango/pangoft2-fontcache.c b/pango/pangoft2-fontcache.c
new file mode 100644
index 00000000..4f7f82b5
--- /dev/null
+++ b/pango/pangoft2-fontcache.c
@@ -0,0 +1,305 @@
+/* Pango
+ * pangoft2-fontcache.c: Cache of FreeType2 faces (FT_Face)
+ *
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "pangoft2-private.h"
+
+/* Font cache
+ */
+
+/* Number of fonts to retain after they are not otherwise referenced.
+ */
+#define CACHE_SIZE 3
+
+typedef struct _CacheEntry CacheEntry;
+
+struct _PangoFT2FontCache
+{
+ FT_Library library;
+
+ GHashTable *forward;
+ GHashTable *back;
+
+ GList *mru;
+ GList *mru_tail;
+ int mru_count;
+};
+
+struct _CacheEntry
+{
+ PangoFT2OA oa;
+ FT_Face face;
+
+ gint ref_count;
+ GList *mru;
+};
+
+static void
+free_cache_entry (PangoFT2OA *oa,
+ CacheEntry *entry,
+ PangoFT2FontCache *cache)
+{
+ FT_Error error;
+
+ PING (("FT_Done_Face (%p)\n", entry->face));
+
+ error = FT_Done_Face (entry->face);
+ if (error != FT_Err_Ok)
+ g_warning ("Error from FT_Done_Face: %s",
+ pango_ft2_ft_strerror (error));
+
+ g_free (entry);
+}
+
+/**
+ * pango_ft2_font_cache_free:
+ * @cache: a #PangoFT2FontCache
+ *
+ * Free a #PangoFT2FontCache and all associated memory. All fonts loaded
+ * through this font cache will be freed along with the cache.
+ **/
+void
+pango_ft2_font_cache_free (PangoFT2FontCache *cache)
+{
+ g_return_if_fail (cache != NULL);
+
+ g_hash_table_foreach (cache->forward, (GHFunc)free_cache_entry, cache);
+
+ g_hash_table_destroy (cache->forward);
+ g_hash_table_destroy (cache->back);
+
+ g_list_free (cache->mru);
+}
+
+static guint
+oa_hash (gconstpointer v)
+{
+ PangoFT2OA *oa = (PangoFT2OA *) v;
+
+ if (oa->open_args->flags & ft_open_memory)
+ return (guint) oa->open_args->memory_base;
+ else if (oa->open_args->flags == ft_open_pathname)
+ return g_str_hash (oa->open_args->pathname);
+ else if (oa->open_args->flags & ft_open_stream)
+ return (guint) oa->open_args->stream;
+ else
+ return 0;
+}
+
+static gint
+oa_equal (gconstpointer v1,
+ gconstpointer v2)
+{
+ PangoFT2OA *oa1 = (PangoFT2OA *) v1;
+ PangoFT2OA *oa2 = (PangoFT2OA *) v2;
+
+ if (oa1->open_args->flags != oa2->open_args->flags)
+ return 0;
+ else if (oa1->open_args->flags & ft_open_memory)
+ return (oa1->open_args->memory_base == oa2->open_args->memory_base &&
+ oa1->face_index == oa2->face_index);
+ else if (oa1->open_args->flags == ft_open_pathname)
+ return (strcmp (oa1->open_args->pathname,
+ oa2->open_args->pathname) == 0 &&
+ oa1->face_index == oa2->face_index);
+ else if (oa1->open_args->flags & ft_open_stream)
+ return (oa1->open_args->stream == oa2->open_args->stream &&
+ oa1->face_index == oa2->face_index);
+ else
+ return 0;
+}
+
+/**
+ * pango_ft2_font_cache_new:
+ *
+ * Create a font cache.
+ *
+ * Return value: The new font cache. This must be freed with
+ * pango_ft2_font_cache_free().
+ **/
+PangoFT2FontCache *
+pango_ft2_font_cache_new (FT_Library library)
+{
+ PangoFT2FontCache *cache;
+
+ cache = g_new (PangoFT2FontCache, 1);
+
+ cache->library = library;
+
+ cache->forward = g_hash_table_new (oa_hash, oa_equal);
+ cache->back = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ cache->mru = NULL;
+ cache->mru_tail = NULL;
+ cache->mru_count = 0;
+
+ return cache;
+}
+
+static void
+cache_entry_unref (PangoFT2FontCache *cache,
+ CacheEntry *entry)
+{
+ entry->ref_count--;
+ PING (("face:%p ref_count:%d\n", entry->face, entry->ref_count));
+ if (entry->ref_count == 0)
+ {
+ g_hash_table_remove (cache->forward, &entry->oa);
+ g_hash_table_remove (cache->back, entry->face);
+
+ free_cache_entry (NULL, entry, cache);
+ }
+}
+
+/**
+ * pango_ft2_font_cache_load:
+ * @cache: a #PangoFT2FontCache
+ *
+ * Load a #FT_Face from #FT_Open_Args and a face index. The
+ * result may be newly loaded, or it may have been previously
+ * stored
+ *
+ * Return value: The #FT_Face, or %NULL if the font could
+ * not be loaded. In order to free this structure, you must call
+ * pango_ft2_font_cache_unload().
+ **/
+FT_Face
+pango_ft2_font_cache_load (PangoFT2FontCache *cache,
+ FT_Open_Args *args,
+ FT_Long face_index)
+{
+ CacheEntry *entry;
+ PangoFT2OA oa;
+
+ g_return_val_if_fail (cache != NULL, NULL);
+ g_return_val_if_fail (args != NULL, NULL);
+
+ oa.open_args = args;
+ oa.face_index = face_index;
+
+ entry = g_hash_table_lookup (cache->forward, &oa);
+
+ if (entry)
+ entry->ref_count++;
+ else
+ {
+ FT_Face face;
+ FT_Error error;
+
+ PING (("FT_Open_Face (%s,%ld)\n", args->pathname, face_index));
+
+ error = FT_Open_Face (cache->library, args, face_index, &face);
+ if (error != FT_Err_Ok)
+ {
+ g_warning ("Error from FT_Open_Face: %s",
+ pango_ft2_ft_strerror (error));
+ return NULL;
+ }
+
+#if DEBUGGING
+ g_print (" = %p\n", face);
+#endif
+
+ entry = g_new (CacheEntry, 1);
+
+ entry->oa = oa;
+ entry->face = face;
+
+ entry->ref_count = 1;
+ entry->mru = NULL;
+
+ g_hash_table_insert (cache->forward, &entry->oa, entry);
+ g_hash_table_insert (cache->back, entry->face, entry);
+ }
+
+ if (entry->mru)
+ {
+ if (cache->mru_count > 1 && entry->mru->prev)
+ {
+ /* Move to the head of the mru list */
+
+ if (entry->mru == cache->mru_tail)
+ {
+ cache->mru_tail = cache->mru_tail->prev;
+ cache->mru_tail->next = NULL;
+ }
+ else
+ {
+ entry->mru->prev->next = entry->mru->next;
+ entry->mru->next->prev = entry->mru->prev;
+ }
+
+ entry->mru->next = cache->mru;
+ entry->mru->prev = NULL;
+ cache->mru->prev = entry->mru;
+ cache->mru = entry->mru;
+ }
+ }
+ else
+ {
+ entry->ref_count++;
+
+ /* Insert into the mru list */
+
+ if (cache->mru_count == CACHE_SIZE)
+ {
+ CacheEntry *old_entry = cache->mru_tail->data;
+
+ cache->mru_tail = cache->mru_tail->prev;
+ cache->mru_tail->next = NULL;
+
+ g_list_free_1 (old_entry->mru);
+ old_entry->mru = NULL;
+ cache_entry_unref (cache, old_entry);
+ }
+ else
+ cache->mru_count++;
+
+ cache->mru = g_list_prepend (cache->mru, entry);
+ if (!cache->mru_tail)
+ cache->mru_tail = cache->mru;
+ entry->mru = cache->mru;
+ }
+
+ return entry->face;
+}
+
+/**
+ * pango_ft2_font_cache_unload:
+ * @cache: a #PangoFT2FontCache
+ * @face: the face to unload
+ *
+ * Free a font structure previously loaded with pango_ft2_font_cache_load()
+ **/
+void
+pango_ft2_font_cache_unload (PangoFT2FontCache *cache,
+ FT_Face face)
+{
+ CacheEntry *entry;
+
+ g_return_if_fail (cache != NULL);
+ g_return_if_fail (face != NULL);
+
+ entry = g_hash_table_lookup (cache->back, face);
+ g_return_if_fail (entry != NULL);
+
+ PING (("pango_ft2_font_cache_unload\n"));
+ cache_entry_unref (cache, entry);
+}