summaryrefslogtreecommitdiff
path: root/libfreetype/ftcmanag.c
diff options
context:
space:
mode:
Diffstat (limited to 'libfreetype/ftcmanag.c')
-rw-r--r--libfreetype/ftcmanag.c765
1 files changed, 765 insertions, 0 deletions
diff --git a/libfreetype/ftcmanag.c b/libfreetype/ftcmanag.c
new file mode 100644
index 00000000..7cdb8f9d
--- /dev/null
+++ b/libfreetype/ftcmanag.c
@@ -0,0 +1,765 @@
+/***************************************************************************/
+/* */
+/* ftcmanag.c */
+/* */
+/* FreeType Cache Manager (body). */
+/* */
+/* Copyright 2000-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include FT_CACHE_MANAGER_H
+#include FT_CACHE_INTERNAL_LRU_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_SIZES_H
+
+#include "ftcerror.h"
+
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cache
+
+#define FTC_LRU_GET_MANAGER( lru ) ( (FTC_Manager)(lru)->user_data )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FACE LRU IMPLEMENTATION *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct FTC_FaceNodeRec_* FTC_FaceNode;
+ typedef struct FTC_SizeNodeRec_* FTC_SizeNode;
+
+
+ typedef struct FTC_FaceNodeRec_
+ {
+ FT_LruNodeRec lru;
+ FT_Face face;
+
+ } FTC_FaceNodeRec;
+
+
+ typedef struct FTC_SizeNodeRec_
+ {
+ FT_LruNodeRec lru;
+ FT_Size size;
+
+ } FTC_SizeNodeRec;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_face_node_init( FTC_FaceNode node,
+ FTC_FaceID face_id,
+ FTC_Manager manager )
+ {
+ FT_Error error;
+
+
+ error = manager->request_face( face_id,
+ manager->library,
+ manager->request_data,
+ &node->face );
+ if ( !error )
+ {
+ /* destroy initial size object; it will be re-created later */
+ if ( node->face->size )
+ FT_Done_Size( node->face->size );
+ }
+
+ return error;
+ }
+
+
+ /* helper function for ftc_face_node_done() */
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_size_node_select( FTC_SizeNode node,
+ FT_Face face )
+ {
+ return FT_BOOL( node->size->face == face );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ftc_face_node_done( FTC_FaceNode node,
+ FTC_Manager manager )
+ {
+ FT_Face face = node->face;
+
+
+ /* we must begin by removing all sizes for the target face */
+ /* from the manager's list */
+ FT_LruList_Remove_Selection( manager->sizes_list,
+ (FT_LruNode_SelectFunc)ftc_size_node_select,
+ face );
+
+ /* all right, we can discard the face now */
+ FT_Done_Face( face );
+ node->face = NULL;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_LruList_ClassRec ftc_face_list_class =
+ {
+ sizeof ( FT_LruListRec ),
+ (FT_LruList_InitFunc)0,
+ (FT_LruList_DoneFunc)0,
+
+ sizeof ( FTC_FaceNodeRec ),
+ (FT_LruNode_InitFunc) ftc_face_node_init,
+ (FT_LruNode_DoneFunc) ftc_face_node_done,
+ (FT_LruNode_FlushFunc) 0, /* no flushing needed */
+ (FT_LruNode_CompareFunc)0, /* direct comparison of FTC_FaceID handles */
+ };
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_Lookup_Face( FTC_Manager manager,
+ FTC_FaceID face_id,
+ FT_Face *aface )
+ {
+ FT_Error error;
+ FTC_FaceNode node;
+
+
+ if ( aface == NULL )
+ return FTC_Err_Bad_Argument;
+
+ *aface = NULL;
+
+ if ( !manager )
+ return FTC_Err_Invalid_Cache_Handle;
+
+ error = FT_LruList_Lookup( manager->faces_list,
+ (FT_LruKey)face_id,
+ (FT_LruNode*)&node );
+ if ( !error )
+ *aface = node->face;
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SIZES LRU IMPLEMENTATION *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ typedef struct FTC_SizeQueryRec_
+ {
+ FT_Face face;
+ FT_UInt width;
+ FT_UInt height;
+
+ } FTC_SizeQueryRec, *FTC_SizeQuery;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_size_node_init( FTC_SizeNode node,
+ FTC_SizeQuery query )
+ {
+ FT_Face face = query->face;
+ FT_Size size;
+ FT_Error error;
+
+
+ node->size = NULL;
+ error = FT_New_Size( face, &size );
+ if ( !error )
+ {
+ FT_Activate_Size( size );
+ error = FT_Set_Pixel_Sizes( query->face,
+ query->width,
+ query->height );
+ if ( error )
+ FT_Done_Size( size );
+ else
+ node->size = size;
+ }
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ftc_size_node_done( FTC_SizeNode node )
+ {
+ if ( node->size )
+ {
+ FT_Done_Size( node->size );
+ node->size = NULL;
+ }
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_size_node_flush( FTC_SizeNode node,
+ FTC_SizeQuery query )
+ {
+ FT_Size size = node->size;
+ FT_Error error;
+
+
+ if ( size->face == query->face )
+ {
+ FT_Activate_Size( size );
+ error = FT_Set_Pixel_Sizes( query->face, query->width, query->height );
+ if ( error )
+ {
+ FT_Done_Size( size );
+ node->size = NULL;
+ }
+ }
+ else
+ {
+ FT_Done_Size( size );
+ node->size = NULL;
+
+ error = ftc_size_node_init( node, query );
+ }
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_size_node_compare( FTC_SizeNode node,
+ FTC_SizeQuery query )
+ {
+ FT_Size size = node->size;
+
+
+ return FT_BOOL( size->face == query->face &&
+ (FT_UInt)size->metrics.x_ppem == query->width &&
+ (FT_UInt)size->metrics.y_ppem == query->height );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_LruList_ClassRec ftc_size_list_class =
+ {
+ sizeof ( FT_LruListRec ),
+ (FT_LruList_InitFunc)0,
+ (FT_LruList_DoneFunc)0,
+
+ sizeof ( FTC_SizeNodeRec ),
+ (FT_LruNode_InitFunc) ftc_size_node_init,
+ (FT_LruNode_DoneFunc) ftc_size_node_done,
+ (FT_LruNode_FlushFunc) ftc_size_node_flush,
+ (FT_LruNode_CompareFunc)ftc_size_node_compare
+ };
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_Lookup_Size( FTC_Manager manager,
+ FTC_Font font,
+ FT_Face *aface,
+ FT_Size *asize )
+ {
+ FT_Error error;
+
+
+ /* check for valid `manager' delayed to FTC_Manager_Lookup_Face() */
+ if ( aface )
+ *aface = 0;
+
+ if ( asize )
+ *asize = 0;
+
+ error = FTC_Manager_Lookup_Face( manager, font->face_id, aface );
+ if ( !error )
+ {
+ FTC_SizeQueryRec query;
+ FTC_SizeNode node;
+
+
+ query.face = *aface;
+ query.width = font->pix_width;
+ query.height = font->pix_height;
+
+ error = FT_LruList_Lookup( manager->sizes_list,
+ (FT_LruKey)&query,
+ (FT_LruNode*)&node );
+ if ( !error )
+ {
+ /* select the size as the current one for this face */
+ FT_Activate_Size( node->size );
+
+ if ( asize )
+ *asize = node->size;
+ }
+ }
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SET TABLE MANAGEMENT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ ftc_family_table_init( FTC_FamilyTable table )
+ {
+ table->count = 0;
+ table->size = 0;
+ table->entries = NULL;
+ table->free = FTC_FAMILY_ENTRY_NONE;
+ }
+
+
+ static void
+ ftc_family_table_done( FTC_FamilyTable table,
+ FT_Memory memory )
+ {
+ FT_FREE( table->entries );
+ table->free = 0;
+ table->count = 0;
+ table->size = 0;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ ftc_family_table_alloc( FTC_FamilyTable table,
+ FT_Memory memory,
+ FTC_FamilyEntry *aentry )
+ {
+ FTC_FamilyEntry entry;
+ FT_Error error = 0;
+
+
+ /* re-allocate table size when needed */
+ if ( table->free == FTC_FAMILY_ENTRY_NONE && table->count >= table->size )
+ {
+ FT_UInt old_size = table->size;
+ FT_UInt new_size, idx;
+
+
+ if ( old_size == 0 )
+ new_size = 8;
+ else
+ {
+ new_size = old_size * 2;
+
+ /* check for (unlikely) overflow */
+ if ( new_size < old_size )
+ new_size = 65534;
+ }
+
+ if ( FT_RENEW_ARRAY( table->entries, old_size, new_size ) )
+ return error;
+
+ table->size = new_size;
+
+ entry = table->entries + old_size;
+ table->free = old_size;
+
+ for ( idx = old_size; idx + 1 < new_size; idx++, entry++ )
+ {
+ entry->link = idx + 1;
+ entry->index = idx;
+ }
+
+ entry->link = FTC_FAMILY_ENTRY_NONE;
+ entry->index = idx;
+ }
+
+ if ( table->free != FTC_FAMILY_ENTRY_NONE )
+ {
+ entry = table->entries + table->free;
+ table->free = entry->link;
+ }
+ else if ( table->count < table->size )
+ {
+ entry = table->entries + table->count++;
+ }
+ else
+ {
+ FT_ERROR(( "ftc_family_table_alloc: internal bug!" ));
+ return FTC_Err_Invalid_Argument;
+ }
+
+ entry->link = FTC_FAMILY_ENTRY_NONE;
+ table->count++;
+
+ *aentry = entry;
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( void )
+ ftc_family_table_free( FTC_FamilyTable table,
+ FT_UInt idx )
+ {
+ /* simply add it to the linked list of free entries */
+ if ( idx < table->count )
+ {
+ FTC_FamilyEntry entry = table->entries + idx;
+
+
+ if ( entry->link != FTC_FAMILY_ENTRY_NONE )
+ FT_ERROR(( "ftc_family_table_free: internal bug!\n" ));
+ else
+ {
+ entry->link = table->free;
+ table->free = entry->index;
+ table->count--;
+ }
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CACHE MANAGER ROUTINES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_New( FT_Library library,
+ FT_UInt max_faces,
+ FT_UInt max_sizes,
+ FT_ULong max_bytes,
+ FTC_Face_Requester requester,
+ FT_Pointer req_data,
+ FTC_Manager *amanager )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FTC_Manager manager = 0;
+
+
+ if ( !library )
+ return FTC_Err_Invalid_Library_Handle;
+
+ memory = library->memory;
+
+ if ( FT_NEW( manager ) )
+ goto Exit;
+
+ if ( max_faces == 0 )
+ max_faces = FTC_MAX_FACES_DEFAULT;
+
+ if ( max_sizes == 0 )
+ max_sizes = FTC_MAX_SIZES_DEFAULT;
+
+ if ( max_bytes == 0 )
+ max_bytes = FTC_MAX_BYTES_DEFAULT;
+
+ error = FT_LruList_New( &ftc_face_list_class,
+ max_faces,
+ manager,
+ memory,
+ &manager->faces_list );
+ if ( error )
+ goto Exit;
+
+ error = FT_LruList_New( &ftc_size_list_class,
+ max_sizes,
+ manager,
+ memory,
+ &manager->sizes_list );
+ if ( error )
+ goto Exit;
+
+ manager->library = library;
+ manager->max_weight = max_bytes;
+ manager->cur_weight = 0;
+
+ manager->request_face = requester;
+ manager->request_data = req_data;
+
+ ftc_family_table_init( &manager->families );
+
+ *amanager = manager;
+
+ Exit:
+ if ( error && manager )
+ {
+ FT_LruList_Destroy( manager->faces_list );
+ FT_LruList_Destroy( manager->sizes_list );
+ FT_FREE( manager );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Manager_Done( FTC_Manager manager )
+ {
+ FT_Memory memory;
+ FT_UInt idx;
+
+
+ if ( !manager || !manager->library )
+ return;
+
+ memory = manager->library->memory;
+
+ /* now discard all caches */
+ for (idx = 0; idx < FTC_MAX_CACHES; idx++ )
+ {
+ FTC_Cache cache = manager->caches[idx];
+
+
+ if ( cache )
+ {
+ cache->clazz->cache_done( cache );
+ FT_FREE( cache );
+ manager->caches[idx] = 0;
+ }
+ }
+
+ /* discard families table */
+ ftc_family_table_done( &manager->families, memory );
+
+ /* discard faces and sizes */
+ FT_LruList_Destroy( manager->faces_list );
+ manager->faces_list = 0;
+
+ FT_LruList_Destroy( manager->sizes_list );
+ manager->sizes_list = 0;
+
+ FT_FREE( manager );
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Manager_Reset( FTC_Manager manager )
+ {
+ if ( manager )
+ {
+ FT_LruList_Reset( manager->sizes_list );
+ FT_LruList_Reset( manager->faces_list );
+ }
+ /* XXX: FIXME: flush the caches? */
+ }
+
+
+#ifdef FT_DEBUG_ERROR
+
+ FT_EXPORT_DEF( void )
+ FTC_Manager_Check( FTC_Manager manager )
+ {
+ FTC_Node node, first;
+
+
+ first = manager->nodes_list;
+
+ /* check node weights */
+ if ( first )
+ {
+ FT_ULong weight = 0;
+
+
+ node = first;
+
+ do
+ {
+ FTC_FamilyEntry entry = manager->families.entries + node->fam_index;
+ FTC_Cache cache;
+
+ if ( (FT_UInt)node->fam_index >= manager->families.count ||
+ entry->link != FTC_FAMILY_ENTRY_NONE )
+ FT_ERROR(( "FTC_Manager_Check: invalid node (family index = %ld\n",
+ node->fam_index ));
+ else
+ {
+ cache = entry->cache;
+ weight += cache->clazz->node_weight( node, cache );
+ }
+
+ node = node->mru_next;
+
+ } while ( node != first );
+
+ if ( weight != manager->cur_weight )
+ FT_ERROR(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
+ manager->cur_weight, weight ));
+ }
+
+ /* check circular list */
+ if ( first )
+ {
+ FT_UFast count = 0;
+
+
+ node = first;
+ do
+ {
+ count++;
+ node = node->mru_next;
+
+ } while ( node != first );
+
+ if ( count != manager->num_nodes )
+ FT_ERROR((
+ "FTC_Manager_Check: invalid cache node count %d instead of %d\n",
+ manager->num_nodes, count ));
+ }
+ }
+
+#endif /* FT_DEBUG_ERROR */
+
+
+ /* `Compress' the manager's data, i.e., get rid of old cache nodes */
+ /* that are not referenced anymore in order to limit the total */
+ /* memory used by the cache. */
+
+ /* documentation is in ftcmanag.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Manager_Compress( FTC_Manager manager )
+ {
+ FTC_Node node, first;
+
+
+ if ( !manager )
+ return;
+
+ first = manager->nodes_list;
+
+#ifdef FT_DEBUG_ERROR
+ FTC_Manager_Check( manager );
+
+ FT_ERROR(( "compressing, weight = %ld, max = %ld, nodes = %d\n",
+ manager->cur_weight, manager->max_weight,
+ manager->num_nodes ));
+#endif
+
+ if ( manager->cur_weight < manager->max_weight || first == NULL )
+ return;
+
+ /* go to last node - it's a circular list */
+ node = first->mru_prev;
+ do
+ {
+ FTC_Node prev = node->mru_prev;
+
+
+ prev = ( node == first ) ? NULL : node->mru_prev;
+
+ if ( node->ref_count <= 0 )
+ ftc_node_destroy( node, manager );
+
+ node = prev;
+
+ } while ( node && manager->cur_weight > manager->max_weight );
+ }
+
+
+ /* documentation is in ftcmanag.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_Register_Cache( FTC_Manager manager,
+ FTC_Cache_Class clazz,
+ FTC_Cache *acache )
+ {
+ FT_Error error = FTC_Err_Invalid_Argument;
+ FTC_Cache cache = NULL;
+
+
+ if ( manager && clazz && acache )
+ {
+ FT_Memory memory = manager->library->memory;
+ FT_UInt idx = 0;
+
+
+ /* check for an empty cache slot in the manager's table */
+ for ( idx = 0; idx < FTC_MAX_CACHES; idx++ )
+ {
+ if ( manager->caches[idx] == 0 )
+ break;
+ }
+
+ /* return an error if there are too many registered caches */
+ if ( idx >= FTC_MAX_CACHES )
+ {
+ error = FTC_Err_Too_Many_Caches;
+ FT_ERROR(( "FTC_Manager_Register_Cache:" ));
+ FT_ERROR(( " too many registered caches\n" ));
+ goto Exit;
+ }
+
+ if ( !FT_ALLOC( cache, clazz->cache_size ) )
+ {
+ cache->manager = manager;
+ cache->memory = memory;
+ cache->clazz = clazz;
+
+ /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */
+ /* IF IT IS NOT SET CORRECTLY */
+ cache->cache_index = idx;
+
+ if ( clazz->cache_init )
+ {
+ error = clazz->cache_init( cache );
+ if ( error )
+ {
+ if ( clazz->cache_done )
+ clazz->cache_done( cache );
+
+ FT_FREE( cache );
+ goto Exit;
+ }
+ }
+
+ manager->caches[idx] = cache;
+ }
+ }
+
+ Exit:
+ *acache = cache;
+ return error;
+ }
+
+
+ /* documentation is in ftcmanag.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Node_Unref( FTC_Node node,
+ FTC_Manager manager )
+ {
+ if ( node && (FT_UInt)node->fam_index < manager->families.count &&
+ manager->families.entries[node->fam_index].cache )
+ {
+ node->ref_count--;
+ }
+ }
+
+
+/* END */