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..
--- /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 */