shithub: freetype+ttf2subf

Download patch

ref: b466a7650ce97d3843bcbb4b145bda323a201022
parent: a39acf55f753b7a6cba7b3f0839505e5f420cea7
author: David Turner <[email protected]>
date: Wed Aug 23 07:22:30 EDT 2000

The FreeType Caching Subsystem - first lines of code

beware, this code is not tested, and probably doesn't compile
correctly.. more information will follow..

git/fs: mount .git/fs: mount/attach disallowed
--- /dev/null
+++ b/src/cache/ftcimage.c
@@ -1,0 +1,147 @@
+#include <cache/ftcimage.h>
+
+  static
+  void       ftc_done_glyph_image( FTC_Image_Queue  queue,
+                                   FTC_ImageNode    node )
+  {
+    FT_UNUSED(queue);
+    FT_Done_Glyph( FTC_IMAGENODE_GET_GLYPH(node) );
+  }
+
+  static
+  FT_ULong   ftc_size_bitmap_image( FTC_Image_Queue  queue,
+                                    FTC_ImageNode    node )
+  {
+    FT_Long         pitch;
+    FT_BitmapGlyph  glyph;
+    
+    FT_UNUSED(queue);
+    glyph = (FT_BitmapGlyph)FTC_IMAGENODE_GET_GLYPH(node);
+    pitch = glyph->bitmap.pitch;
+    if (pitch < 0)
+      pitch = -pitch;
+      
+    return (FT_ULong)(pitch * glyph->bitmap->rows + sizeof(*glyph));
+  }
+
+  static
+  FT_ULong   ftc_size_outline_image( FTC_Image_Queue  queue,
+                                     FTC_ImageNode    node )
+  {
+    FT_Long          pitch;
+    FT_OutlineGlyph  glyph;
+    FT_Outline*      outline;
+    
+    FT_UNUSED(queue);
+    glyph   = (FT_OutlineGlyph)FTC_IMAGENODE_GET_GLYPH(node);
+    outline = &glyph->outline;
+    
+    return (FT_ULong)( 
+           outline->n_points *  (sizeof(FT_Vector)+sizeof(FT_Byte))  +
+           outline->n_contours * sizeof(FT_Short)                    +
+           sizeof(*glyph) );
+  }
+
+ /*******************************************************************/
+ /*******************************************************************/
+ /*****                                                         *****/
+ /*****              MONOCHROME BITMAP CALLBACKS                *****/
+ /*****                                                         *****/
+ /*******************************************************************/
+ /*******************************************************************/
+
+  static
+  FT_Error   ftc_init_mono_image( FTC_Image_Queue  queue,
+                                  FTC_ImageNode    node )
+  {  
+    FT_Face      face;
+    FT_Size      size;
+    FT_Error     error;
+
+    
+    error = FTC_Manager_Lookup_Size( queue->manager,
+                                     &queue->size_rec,
+                                     &face, &size );
+    if (!error)
+    {
+      FT_UInt  glyph_index = FTC_IMAGENODE_GINDEX(node);
+
+      
+      error = FT_Load_Glyph( face, glyph_index,
+                             FT_LOAD_RENDER | FT_LOAD_MONOCHROME );
+      if (!error)
+      {
+        if ( face->glyph->format            != ft_image_format_bitmap ||
+             face->glyph->bitmap.pixel_mode != ft_pixel_mode_mono )
+        {
+          /* there is no monochrome glyph for this font !! */
+          error = FT_Err_Invalid_Glyph_Index;
+        }
+        else
+        {
+          /* ok, copy it */
+          FT_Glyph  glyph;
+          
+          
+          error = FT_Get_Glyph( face->glyph, &glyph );
+          if (!error)
+            FTC_IMAGENODE_SET_GLYPH(node,glyph);
+        }
+      }
+    }
+    return error;
+  }
+
+
+  static
+  FT_Error   ftc_init_gray_image( FTC_Image_Queue  queue,
+                                  FTC_ImageNode    node )
+  {  
+    FT_Face      face;
+    FT_Size      size;
+    FT_Error     error;
+    
+
+    error = FTC_Manager_Lookup_Size( queue->manager,
+                                     &queue->size_rec,
+                                     &face, &size );
+    if (!error)
+    {
+      FT_UInt  glyph_index = FTC_IMAGENODE_GINDEX(node);
+      
+
+      error = FT_Load_Glyph( face, glyph_index,
+                             FT_LOAD_RENDER );
+      if (!error)
+      {
+        if ( face->glyph->format            != ft_image_format_bitmap ||
+             face->glyph->bitmap.pixel_mode != ft_pixel_mode_grays )
+        {
+          /* there is no monochrome glyph for this font !! */
+          error = FT_Err_Invalid_Glyph_Index;
+        }
+        else
+        {
+          /* ok, copy it */
+          FT_Glyph  glyph;
+          
+          
+          error = FT_Get_Glyph( face->glyph, &glyph );
+          if (!error)
+            FTC_IMAGENODE_SET_GLYPH(node,glyph);
+        }
+      }
+    }
+    return error;
+  }
+
+
+ /*******************************************************************/
+ /*******************************************************************/
+ /*****                                                         *****/
+ /*****              MONOCHROME BITMAP CALLBACKS                *****/
+ /*****                                                         *****/
+ /*******************************************************************/
+ /*******************************************************************/
+
+  
--- /dev/null
+++ b/src/cache/ftcimage.h
@@ -1,0 +1,104 @@
+#ifndef FTCIMAGE_H
+#define FTCIMAGE_H
+
+#include <cache/ftcmanag.h>
+#include <freetype/ftglyph.h>
+
+  typedef struct FTC_Image_QueueRec_*     FTC_Image_Queue;
+  typedef struct FTC_Image_CacheRec_*     FTC_Image_Cache; 
+  typedef struct FTC_ImageNodeRec_*       FTC_ImageNode;
+  
+  /* types of glyph images */
+  typedef enum FTC_Image_Type_
+  {
+    ftc_image_mono = 0,         /* monochrome bitmap   */
+    ftc_image_grays,            /* anti-aliased bitmap */
+    ftc_image_outline,          /* scaled outline      */
+    ftc_image_master_outline,   /* original outline    */
+  
+  } FTC_Image_Type;
+
+ 
+  /* a descriptor used to describe all glyphs in a given queue */
+  typedef struct FTC_Image_Desc_
+  {
+    FTC_FaceID   face_id;
+    FT_UInt      pix_width;
+    FT_UInt      pix_height;
+    FT_UInt      image_type;
+  
+  } FTC_Image_Desc;
+
+/* macros used to pack a glyph index and a queue index in a single ptr */  
+#define  FTC_PTR_TO_GINDEX(p)   ((FT_UInt)((FT_ULong)(p) >> 16))
+#define  FTC_PTR_TO_QINDEX(p)   ((FT_UInt)((FT_ULong)(p) & 0xFFFF)) 
+#define  FTC_INDICES_TO_PTR(g,q)  ((FT_Pointer)(((FT_ULong)(g) << 16) |  \
+                                               ((FT_ULong)(q) & 0xFFFF)))
+    
+  typedef struct FTC_ImageNodeRec_
+  {
+    FT_ListNodeRec  root1;  /* root1.data contains a FT_Glyph handle           */
+    FT_ListNodeRec  root2;  /* root2.data contains a glyph index + queue index */
+ 
+  } FTC_ImageNodeRec;
+
+/* macros to read/set the glyph & queue index in a FTC_ImageNode */
+#define  FTC_IMAGENODE_GET_GINDEX(n)  FTC_PTR_TO_GINDEX((n)->root2.data)
+#define  FTC_IMAGENODE_GET_QINDEX(n)  FTC_PTR_TO_QINDEX((n)->root2.data)
+#define  FTC_IMAGENODE_SET_INDICES(g,q)   \
+            do { (n)->root2.data = FTC_INDICES_TO_PTR(g,q); } while (0)
+
+
+  typedef struct FTC_Image_CacheRec_
+  {
+    FTC_Manager  manager;
+    FT_Memory    memory;
+    
+    FT_ULong     max_bytes;   /* maximum size of cache in bytes */
+    FT_ULong     num_bytes;   /* current size of cache in bytes */
+    
+    FT_Lru       queues_lru;  /* static queues lru list */
+
+  } FTC_Image_Cache;
+
+
+ /* a table of functions used to generate/manager glyph images */
+  typedef struct FTC_Image_Class_
+  {
+    FT_Error     (*init_image)( FTC_Image_Queue  queue,
+                                FTC_Image_Node   node );
+                                
+    void         (*done_image)( FTC_Image_Queue  queue,
+                                FTC_Image_Node   node );
+                                
+    FT_ULong     (*size_image)( FTC_Image_Queue  queue,
+                                FTC_Image_Node   node );
+
+  } FTC_Image_Class;
+
+
+  typedef struct FTC_Image_QueueRec_
+  {
+    FTC_Image_Cache   cache;
+    FTC_Manager       manager;
+    FT_Memory         memory;
+    FTC_Image_Class*  clazz;
+    FTC_Image_Desc    descriptor;
+    FT_UInt           hash_size;
+    FT_List           buckets;
+
+  } FTC_Image_SubCacheRec;
+
+
+  FT_EXPORT_DEF(FT_Error) FTC_Image_Cache_New( FTC_Manager       manager,
+                                               FT_ULong          max_bytes,
+                                               FTC_Image_Cache  *acache );
+                                                
+  FT_EXPORT_DEF(void)     FTC_Image_Cache_Done( FTC_Image_Cache  cache );
+  
+  FT_EXPORT_DEF(FT_Error) FTC_Image_Cache_Lookup( FTC_Image_Cache  cache,
+                                                  FTC_Image_Desc*  desc,
+                                                  FT_UInt          gindex,
+                                                  FT_Glyph        *aglyph );
+
+#endif /* FTCIMAGE_H */
--- /dev/null
+++ b/src/cache/ftcmanag.c
@@ -1,0 +1,280 @@
+#include <cache/ftcmanag.h>
+#include <freetype/internal/ftobjs.h>
+
+#define FTC_LRU_GET_MANAGER(lru) ((FTC_Manager_Lru)lru)->manager
+
+ /*******************************************************************/
+ /*******************************************************************/
+ /*****                                                         *****/
+ /*****               FACE & SIZE LRU CALLBACKS                 *****/
+ /*****                                                         *****/
+ /*******************************************************************/
+ /*******************************************************************/
+
+  static
+  FT_Error  ftc_manager_init_face( FT_Lru      lru,
+                                   FT_LruNode  node )
+  {
+    FTC_Manager  manager = FTC_LRU_GET_MANAGER(lru);
+    FT_Error     error;
+    
+    error = manager->request_face( (FTC_FaceID)node->key,
+                                   manager->request_data,
+                                   (FT_Face*)&node->root.data );
+    if (!error)
+    {
+      /* destroy initial size object, it will be re-created later */
+      FT_Face  face = (FT_Face)node->root.data;
+      FT_Done_Size( face->size );
+    }
+    return error;
+  }
+
+
+      /* helper function for ftc_manager_done_face */
+      static
+      FT_Bool  ftc_manager_size_selector( FT_Lru      lru,
+                                          FT_LruNode  node,
+                                          FT_Pointer  data )
+      {
+        FT_UNUSED(lru);
+        return ((FT_Size)node->root.data)->face == (FT_Face)data;
+      }
+
+  static
+  void      ftc_manager_done_face( FT_Lru      lru,
+                                   FT_LruNode  node )
+  {
+    FTC_Manager  manager = FTC_LRU_GET_MANAGER(lru);
+    FT_Face      face    = (FT_Face)node->root.data;
+    
+    /* we must begin by removing all sizes for the target face */
+    /* from the manager's list..                               */
+    FT_Lru_Remove_Selection( manager->sizes_lru,
+                             ftc_manager_size_selector,
+                             face );
+
+    /* all right, we can discard the face now */    
+    FT_Done_Face( face );
+    node->root.data = 0;
+  }
+
+
+  typedef struct FTC_SizeRequest_
+  {
+    FT_Face    face;
+    FT_UShort  width;
+    FT_UShort  height;
+    
+  } FTC_SizeRequest;
+
+
+  static
+  FT_Error   ftc_manager_init_size( FT_Lru        lru,
+                                    FT_LruNode    node )
+  {
+    FTC_SizeRequest*  size_req = (FTC_SizeRequest*)node->key;
+    FT_Size           size;
+    FT_Error          error;
+    
+    FT_UNUSED(lru);
+    
+    node->root.data = 0;
+    error = FT_New_Size( size_req->face, &size );
+    if (!error)
+    {
+      error = FT_Set_Pixel_Sizes( size_req->face,
+                                  size_req->width,
+                                  size_req->height );
+      if (error)
+        FT_Done_Size(size);
+      else
+        node->root.data = size;
+    }
+    return error;   
+  }
+
+
+  static
+  void       ftc_manager_done_size( FT_Lru      lru,
+                                    FT_LruNode  node )
+  {
+    FT_UNUSED(lru);
+    FT_Done_Size( (FT_Size)node->root.data );
+  }                                
+
+
+
+  static
+  FT_Error   ftc_manager_flush_size( FT_Lru      lru,
+                                     FT_LruNode  node,
+                                     FT_LruKey   key )
+  {
+    FTC_SizeRequest*  req  = (FTC_SizeRequest*)key;
+    FT_Size           size = (FT_Size)node->root.data;
+    FT_Error          error;
+    
+    if ( size->face == req->face )
+    {
+      size->face->size = size;  /* set current size */
+      error = FT_Set_Pixel_Sizes( req->face, req->width, req->height );
+      if (error)
+        FT_Done_Size( size );
+    }
+    else
+    {
+      FT_Done_Size(size);
+      node->key = key;
+      error = ftc_manager_init_size( lru, node );
+    }
+    return error;
+  }
+
+
+  static
+  FT_Bool    ftc_manager_compare_size( FT_LruNode  node,
+                                       FT_LruKey   key )
+  {
+    FTC_SizeRequest*  req  = (FTC_SizeRequest*)key;
+    FT_Size           size = (FT_Size)node->root.data;
+    
+    FT_UNUSED(node);
+    return ( size->face           == req->face &&
+             size->metrics.x_ppem == req->width &&
+             size->metrics.y_ppem == req->height );
+  }
+
+
+
+  
+  static
+  const FT_Lru_Class  ftc_face_lru_class =
+  {
+    sizeof( FTC_Manager_LruRec ),
+    ftc_manager_init_face,
+    ftc_manager_done_face,
+    0,
+    0
+  };
+
+  
+  static
+  const FT_Lru_Class  ftc_size_lru_class =
+  {
+    sizeof( FTC_Manager_LruRec ),
+    ftc_manager_init_size,
+    ftc_manager_done_size,
+    ftc_manager_flush_size,
+    ftc_manager_compare_size
+  };
+
+
+
+  FT_EXPORT_FUNC(FT_Error)  FTC_Manager_New  ( FT_Library          library,
+                                               FTC_Face_Requester  requester,
+                                               FT_Pointer          req_data,
+                                               FTC_Manager        *amanager )
+  {
+    FT_Error     error;
+    FT_Memory    memory  = library->memory;
+    FTC_Manager  manager = 0;
+    
+    
+    if ( ALLOC( manager, sizeof(*manager) ) )
+      goto Exit;
+      
+    error = FT_Lru_New( &ftc_face_lru_class,
+                        FTC_MAX_FACES,
+                        memory,
+                        1, /* pre_alloc = TRUE */
+                        (FT_Lru*)&manager->faces_lru );
+    if (error)
+      goto Exit;
+      
+    error = FT_Lru_New( &ftc_size_lru_class,
+                        FTC_MAX_SIZES,
+                        memory,
+                        1, /* pre_alloc = TRUE */
+                        (FT_Lru*)&manager->sizes_lru );                    
+    if (error)
+      goto Exit;
+    
+    ((FTC_Manager_Lru)manager->faces_lru)->manager = manager;
+    ((FTC_Manager_Lru)manager->sizes_lru)->manager = manager;
+
+    manager->library      = library;
+    manager->request_face = requester;
+    manager->request_data = req_data;
+    *amanager = manager;
+    
+  Exit:
+    if (error && manager)
+    {
+      FT_Lru_Done( manager->sizes_lru );
+      FT_Lru_Done( manager->faces_lru );
+      FREE( manager );
+    }
+     
+    return error;
+  }
+                        
+  FT_EXPORT_DEF(void)      FTC_Manager_Done ( FTC_Manager  manager )
+  {
+    FT_Memory  memory = manager->library->memory;
+    
+    FT_Lru_Done( manager->sizes_lru );
+    FT_Lru_Done( manager->faces_lru );
+    FREE( manager );
+  }
+
+
+  FT_EXPORT_DEF(void)      FTC_Manager_Reset( FTC_Manager  manager )
+  {
+    FT_Lru_Reset( manager->sizes_lru );
+    FT_Lru_Reset( manager->faces_lru );
+  }
+
+
+  FT_EXPORT_DEF(FT_Error)  FTC_Manager_Lookup_Face( FTC_Manager  manager,
+                                                    FTC_FaceID   face_id,
+                                                    FT_Face     *aface )
+  {
+    return  FT_Lru_Lookup( manager->faces_lru,
+                          (FT_LruKey)face_id, 
+                          (FT_Pointer*)aface );
+  }
+ 
+ 
+ 
+  FT_EXPORT_DEF(FT_Error)  FTC_Manager_Lookup_Size( FTC_Manager  manager,
+                                                    FTC_SizeID   size_id,
+                                                    FT_Face     *aface,
+                                                    FT_Size     *asize )
+  {
+    FTC_SizeRequest  req;
+    FT_Error         error;
+    FT_Face          face;
+    
+    *aface = 0;
+    *asize = 0;
+    error  = FTC_Manager_Lookup_Face( manager, size_id->face_id, &face ); 
+    if (!error)
+    {
+      req.face   = face;
+      req.width  = size_id->pix_width;
+      req.height = size_id->pix_height;
+      
+      error  = FT_Lru_Lookup( manager->sizes_lru,
+                              (FT_LruKey)&req,
+                              (FT_Pointer*)asize );
+      if (!error)
+      {
+        /* select the size as the current one for this face */
+        face->size = *asize;
+        *aface = face;
+      }
+    }
+    return error;
+  }
+
+
--- /dev/null
+++ b/src/cache/ftcmanag.h
@@ -1,0 +1,65 @@
+#ifndef FTCMANAG_H
+#define FTCMANAG_H
+
+#include <cache/ftlru.h>
+
+#define  FTC_MAX_FACES  4
+#define  FTC_MAX_SIZES  8
+
+  typedef FT_Pointer  FTC_FaceID;
+  
+  typedef struct FTC_SizeRec_
+  {
+    FTC_FaceID   face_id;
+    FT_UShort    pix_width;
+    FT_UShort    pix_height;
+    
+  } FTC_SizeRec, *FTC_SizeID;
+
+ 
+  typedef FT_Error  (*FTC_Face_Requester)( FTC_FaceID  face_id,
+                                           FT_Pointer  data, 
+                                           FT_Face    *aface );
+
+
+  typedef struct FTC_ManagerRec_*  FTC_Manager;
+
+  typedef struct FTC_Manager_LruRec_
+  {
+    FT_LruRec    root;
+    FTC_Manager  manager;
+  
+  } FTC_Manager_LruRec, *FTC_Manager_Lru;
+
+
+  typedef struct FTC_ManagerRec_
+  {
+    FT_Library          library;
+    FT_Lru              faces_lru;
+    FT_Lru              sizes_lru;
+    
+    FT_Pointer          request_data;
+    FTC_Face_Requester  request_face;
+    
+  } FTC_ManagerRec;
+
+
+  FT_EXPORT_DEF(FT_Error)  FTC_Manager_New  ( FT_Library          library,
+                                              FTC_Face_Requester  requester,
+                                              FT_Pointer          req_data,
+                                              FTC_Manager        *amanager );
+                        
+  FT_EXPORT_DEF(void)      FTC_Manager_Done ( FTC_Manager  manager );
+
+  FT_EXPORT_DEF(void)      FTC_Manager_Reset( FTC_Manager  manager );
+
+  FT_EXPORT_DEF(FT_Error)  FTC_Manager_Lookup_Face( FTC_Manager  manager,
+                                                    FTC_FaceID   face_id,
+                                                    FT_Face     *aface );
+ 
+  FT_EXPORT_DEF(FT_Error)  FTC_Manager_Lookup_Size( FTC_Manager  manager,
+                                                    FTC_SizeID   size_id,
+                                                    FT_Face     *aface,
+                                                    FT_Size     *asize );
+
+#endif /* FTCMANAG_H */
--- /dev/null
+++ b/src/cache/ftlru.c
@@ -1,0 +1,263 @@
+#include <cache/ftlru.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftlist.h>
+
+  static
+  void  lru_build_free_list( FT_LruNode  nodes,
+                             FT_UInt     count,
+                             FT_List     free_list )
+  {
+    FT_LruNode  node  = nodes;
+    FT_LruNode  limit = node + count;
+    
+    free_list->head = free_list->tail = 0;
+    for ( ; node < limit; node++ )
+      FT_List_Add( free_list, (FT_ListNode)node );
+  }
+
+
+  FT_EXPORT_FUNC(FT_Error)  FT_Lru_New   ( const FT_Lru_Class*  clazz,
+                                           FT_UInt        max_elements,
+                                           FT_Memory      memory,
+                                           FT_Bool        pre_alloc,
+                                           FT_Lru        *alru )
+  {
+    FT_Error  error;
+    FT_Lru    lru;
+    
+    *alru = 0;
+    if ( !ALLOC( lru, sizeof(*lru) ) )
+    {
+      if (pre_alloc)
+      {
+        /* allocate static array of lru list nodes */
+        if ( ALLOC_ARRAY( lru->nodes, max_elements, FT_LruNodeRec ) )
+        {
+          FREE( lru );
+          goto Exit;
+        }
+        
+        /* build the 'free_nodes' list from the array */
+        lru_build_free_list( lru->nodes, max_elements, &lru->free_nodes );
+      }
+      
+      /* initialize common fields */
+      lru->clazz        = (FT_Lru_Class*)clazz;
+      lru->max_elements = max_elements;
+      lru->memory       = memory;
+      *alru = lru;
+    }
+  Exit:
+    return error;
+  }
+
+
+                                         
+  FT_EXPORT_DEF(void)      FT_Lru_Reset ( FT_Lru    lru )
+  {
+    FT_ListNode    node   = lru->elements.head;
+    FT_Lru_Class*  clazz  = lru->clazz;
+    FT_Memory      memory = lru->memory;
+    
+    while (node)
+    {
+      FT_ListNode  next = node->next;
+      
+      clazz->done_element( lru, (FT_LruNode)node );
+      if (!lru->nodes)
+        FREE(node);
+      
+      node = next;
+    }
+    
+    /* rebuild free list if necessary */
+    if (lru->nodes)
+      lru_build_free_list( lru->nodes, lru->max_elements, &lru->free_nodes );
+                   lru->elements.head = lru->elements.tail = 0;
+    lru->num_elements  = 0;
+  }
+
+
+                                  
+  FT_EXPORT_DEF(void)      FT_Lru_Done  ( FT_Lru  lru )
+  {
+    FT_Memory  memory = lru->memory;
+    
+    FT_Lru_Reset(lru);
+    FREE(lru);
+  }
+
+
+
+  FT_EXPORT_DEF(FT_Error)  FT_Lru_Lookup_Node( FT_Lru        lru,
+                                               FT_LruKey     key,
+                                               FT_LruNode*  anode )
+  {
+    FT_Error       error  = 0;
+    FT_ListNode    node   = lru->elements.head;
+    FT_Lru_Class*  clazz  = lru->clazz;
+    FT_LruNode     found  = 0; 
+    FT_Memory      memory = lru->memory;
+    
+    if (clazz->compare_element)
+    {
+      for ( ; node; node = node->next )
+        if (clazz->compare_element( (FT_LruNode)node, key ))
+        {
+          found = (FT_LruNode)node;
+          break;
+        }
+    }
+    else
+    {
+      for ( ; node; node = node->next )
+        if (((FT_LruNode)node)->key == key)
+        {
+          found = (FT_LruNode)node;
+          break;
+        }
+    }
+   
+    if (!found)
+    {
+      /* we didn't find the relevant element. We will now try */
+      /* to create a new one..                                */
+      if ( lru->num_elements >= lru->max_elements )
+      {
+        /* this lru list is full, we will now flush */
+        /* the oldest node                          */
+        FT_LruNode  lru_node;
+        
+        
+        node     = lru->elements.tail;
+        lru_node = (FT_LruNode)node;
+        
+        if (clazz->flush_element)
+          error = clazz->flush_element( lru, lru_node, key );
+        else
+        {
+          clazz->done_element( lru, lru_node );
+          lru_node->key = key;
+          node->data    = 0;
+          error = clazz->init_element( lru, lru_node );
+        }
+
+        if (!error)
+        {
+          /* now, move element to top of list */
+          FT_List_Up( &lru->elements, node );
+        }
+        else
+        {
+          /* in case of error, the node must be discarded */
+          FT_List_Remove( &lru->elements, node );
+          lru->num_elements--;
+        
+          if (lru->nodes)
+            FT_List_Insert( &lru->free_nodes, node );
+          else
+            FREE( lru_node );
+        }
+      }
+      else
+      { 
+        FT_LruNode  lru_node;
+        
+        /* create a new lru list node, then the element for it */
+        if (lru->nodes)
+        {
+          node     = lru->free_nodes.head;
+          lru_node = (FT_LruNode)node;
+          lru_node->key = key;
+          
+          error = clazz->init_element( lru, lru_node );
+          if (error)
+            goto Exit;
+            
+          FT_List_Remove( &lru->free_nodes, node );
+        }
+        else
+        {
+          if ( ALLOC( lru_node, sizeof(*lru_node) ) )
+            goto Exit;
+            
+          lru_node->key = key;
+          error = clazz->init_element( lru, lru_node );
+          if (error)
+          {
+            FREE( lru_node );
+            goto Exit;
+          }
+        }
+       
+        found = lru_node; 
+        node  = (FT_ListNode)lru_node;
+        FT_List_Insert( &lru->elements, node );
+        lru->num_elements++;
+      }
+    }
+    
+  Exit: 
+    *anode = found;
+    return error; 
+  }
+
+
+  FT_EXPORT_DEF(FT_Error)  FT_Lru_Lookup( FT_Lru      lru,
+                                          FT_LruKey   key,
+                                          FT_Pointer *aobject )
+  {
+    FT_Error    error;
+    FT_LruNode  node;
+    
+    *aobject = 0;
+    error = FT_Lru_Lookup_Node( lru, key, &node );
+    if (!error)
+      *aobject = node->root.data;
+    
+    return error;
+  }
+
+
+  FT_EXPORT_FUNC(void)  FT_Lru_Remove_Node( FT_Lru      lru,
+                                            FT_LruNode  node )
+  {
+    if (lru->num_elements > 0)
+    {
+      FT_List_Remove( &lru->elements, (FT_ListNode)node );
+      lru->clazz->done_element( lru, node );
+      
+      if (lru->nodes)
+        FT_List_Insert( &lru->free_nodes, (FT_ListNode)node );
+      else
+      {
+        FT_Memory  memory = lru->memory;
+        FREE(node);
+      }
+      lru->num_elements--;
+    }
+  }
+
+
+  FT_EXPORT_FUNC(void) FT_Lru_Remove_Selection( FT_Lru           lru,
+                                                FT_Lru_Selector  selector,
+                                                FT_Pointer       data )
+  {
+    if (lru->num_elements > 0)
+    {
+      FT_ListNode    node = lru->elements.head;
+      FT_ListNode    next;
+      
+      while (node)
+      {
+        next = node->next;
+        if ( selector( lru, (FT_LruNode)node, data ) )
+        {
+          /* remove this element from the list, and destroy it */
+          FT_Lru_Remove_Node( lru, (FT_LruNode)node );
+        }
+        node = next;
+      }
+    }
+  }
+
--- /dev/null
+++ b/src/cache/ftlru.h
@@ -1,0 +1,83 @@
+#ifndef FTLRU_H
+#define FTLRU_H
+
+#include <freetype/freetype.h>
+
+
+  typedef FT_Pointer  FT_LruKey;
+
+
+  typedef struct FT_LruNodeRec_
+  {
+    FT_ListNodeRec  root;
+    FT_LruKey       key;
+  
+  } FT_LruNodeRec, *FT_LruNode;
+
+
+  typedef struct FT_LruRec_*  FT_Lru;
+
+  typedef struct FT_Lru_Class_
+  {
+    FT_UInt        lru_size;      /* object size in bytes */
+    
+    FT_Error     (*init_element)( FT_Lru      lru,
+                                  FT_LruNode  node );
+                                  
+    void         (*done_element)( FT_Lru      lru,
+                                  FT_LruNode  node );
+    
+    FT_Error     (*flush_element)( FT_Lru      lru,
+                                   FT_LruNode  node,
+                                   FT_LruKey   new_key );  
+                                   
+    FT_Bool      (*compare_element)( FT_LruNode  node,
+                                     FT_LruKey   key );
+  } FT_Lru_Class;
+
+
+  typedef FT_Bool  (*FT_Lru_Selector)( FT_Lru      lru,
+                                       FT_LruNode  node,
+                                       FT_Pointer  data );
+
+  typedef struct FT_LruRec_
+  {
+    FT_Lru_Class*   clazz;
+    FT_UInt         max_elements;
+    FT_UInt         num_elements;
+    FT_ListRec      elements;
+    FT_Memory       memory;
+    
+    /* the following fields are only meaningful for static lru containers */
+    FT_ListRec      free_nodes;
+    FT_LruNode      nodes;
+    
+  } FT_LruRec;
+
+
+  FT_EXPORT_DEF(FT_Error)  FT_Lru_New   ( const FT_Lru_Class*  clazz,
+                                          FT_UInt        max_elements,
+                                          FT_Memory      memory,
+                                          FT_Bool        pre_alloc,
+                                          FT_Lru        *alru );
+                                          
+  FT_EXPORT_DEF(void)      FT_Lru_Reset ( FT_Lru    lru ); 
+                                       
+  FT_EXPORT_DEF(void)      FT_Lru_Done  ( FT_Lru  lru ); 
+
+  FT_EXPORT_DEF(FT_Error)  FT_Lru_Lookup_Node( FT_Lru        lru,
+                                               FT_LruKey     key,
+                                               FT_LruNode*  anode );
+
+  FT_EXPORT_DEF(FT_Error)  FT_Lru_Lookup( FT_Lru      lru,
+                                          FT_LruKey   key,
+                                          FT_Pointer *aobject );
+ 
+  FT_EXPORT_DEF(void)      FT_Lru_Remove_Node( FT_Lru      lru,
+                                               FT_LruNode  node );  
+
+  FT_EXPORT_DEF(void) FT_Lru_Remove_Selection( FT_Lru           lru,
+                                               FT_Lru_Selector  selector,
+                                               FT_Pointer       data );
+
+#endif /* FTLRU_H */