ref: 7e10ee403c008f0b080f5e685f16f0f828a15d00
dir: /src/cache/ftcmanag.c/
/***************************************************************************/ /* */ /* ftcmanag.c */ /* */ /* FreeType Cache Manager (body). */ /* */ /* Copyright 2000 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 <freetype/cache/ftcmanag.h> #include <freetype/internal/ftobjs.h> #include <freetype/internal/ftdebug.h> #include <freetype/ftlist.h> #undef FT_COMPONENT #define FT_COMPONENT trace_cache #define FTC_LRU_GET_MANAGER( lru ) (FTC_Manager)lru->user_data /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** FACE & SIZE LRU CALLBACKS *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ FT_CALLBACK_DEF FT_Error ftc_manager_init_face( FT_Lru lru, FT_LruNode node ) { FTC_Manager manager = FTC_LRU_GET_MANAGER( lru ); FT_Error error; FT_Face face; error = manager->request_face( (FTC_FaceID)node->key, manager->library, manager->request_data, (FT_Face*)&node->root.data ); if ( !error ) { /* destroy initial size object; it will be re-created later */ face = (FT_Face)node->root.data; if ( face->size ) FT_Done_Size( face->size ); } return error; } /* helper function for ftc_manager_done_face() */ FT_CALLBACK_DEF 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; } FT_CALLBACK_DEF 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_FontRequest_ { FT_Face face; FT_UShort width; FT_UShort height; } FTC_FontRequest; FT_CALLBACK_DEF FT_Error ftc_manager_init_size( FT_Lru lru, FT_LruNode node ) { FTC_FontRequest* font_req = (FTC_FontRequest*)node->key; FT_Size size; FT_Error error; FT_Face face = font_req->face; FT_UNUSED( lru ); node->root.data = 0; error = FT_New_Size( face, &size ); if ( !error ) { face->size = size; error = FT_Set_Pixel_Sizes( face, font_req->width, font_req->height ); if ( error ) FT_Done_Size( size ); else node->root.data = size; } return error; } FT_CALLBACK_DEF void ftc_manager_done_size( FT_Lru lru, FT_LruNode node ) { FT_UNUSED( lru ); FT_Done_Size( (FT_Size)node->root.data ); } FT_CALLBACK_DEF FT_Error ftc_manager_flush_size( FT_Lru lru, FT_LruNode node, FT_LruKey key ) { FTC_FontRequest* req = (FTC_FontRequest*)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; } FT_CALLBACK_DEF FT_Bool ftc_manager_compare_size( FT_LruNode node, FT_LruKey key ) { FTC_FontRequest* req = (FTC_FontRequest*)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 ); } FT_CALLBACK_TABLE_DEF const FT_Lru_Class ftc_face_lru_class = { sizeof ( FT_LruRec ), ftc_manager_init_face, ftc_manager_done_face, 0, 0 }; FT_CALLBACK_TABLE_DEF const FT_Lru_Class ftc_size_lru_class = { sizeof ( FT_LruRec ), ftc_manager_init_size, ftc_manager_done_size, ftc_manager_flush_size, ftc_manager_compare_size }; 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 FT_Err_Invalid_Library_Handle; memory = library->memory; if ( ALLOC( manager, sizeof ( *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_Lru_New( &ftc_face_lru_class, max_faces, manager, memory, 1, /* pre_alloc = TRUE */ (FT_Lru*)&manager->faces_lru ); if ( error ) goto Exit; error = FT_Lru_New( &ftc_size_lru_class, max_sizes, manager, memory, 1, /* pre_alloc = TRUE */ (FT_Lru*)&manager->sizes_lru ); if ( error ) goto Exit; manager->library = library; manager->max_bytes = max_bytes; 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( void ) FTC_Manager_Done( FTC_Manager manager ) { FT_Memory memory; FT_UInt index; if ( !manager || !manager->library ) return; memory = manager->library->memory; /* now discard all caches */ for (index = 0; index < FTC_MAX_CACHES; index++ ) { FTC_Cache cache = manager->caches[index]; if ( cache ) { cache->clazz->done_cache( cache ); FREE( cache ); manager->caches[index] = 0; } } /* discard faces and sizes */ FT_Lru_Done( manager->sizes_lru ); FT_Lru_Done( manager->faces_lru ); FREE( manager ); } FT_EXPORT( void ) FTC_Manager_Reset( FTC_Manager manager ) { if (manager ) { FT_Lru_Reset( manager->sizes_lru ); FT_Lru_Reset( manager->faces_lru ); } /* XXX: FIXME: flush the caches? */ } FT_EXPORT( FT_Error ) FTC_Manager_Lookup_Face( FTC_Manager manager, FTC_FaceID face_id, FT_Face *aface ) { if ( !manager ) return FT_Err_Invalid_Cache_Handle; return FT_Lru_Lookup( manager->faces_lru, (FT_LruKey)face_id, (FT_Pointer*)aface ); } FT_EXPORT( FT_Error ) FTC_Manager_Lookup_Size( FTC_Manager manager, FTC_Font font, FT_Face *aface, FT_Size *asize ) { FTC_FontRequest req; 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 ) { FT_Size size; req.face = *aface; req.width = font->pix_width; req.height = font->pix_height; error = FT_Lru_Lookup( manager->sizes_lru, (FT_LruKey)&req, (FT_Pointer*)&size ); if ( !error ) { /* select the size as the current one for this face */ (*aface)->size = size; if ( asize ) *asize = size; } } return 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. */ FT_EXPORT_DEF( void ) FTC_Manager_Compress( FTC_Manager manager ) { FT_ListNode node; node = manager->global_lru.tail; while ( manager->num_bytes > manager->max_bytes && node ) { FTC_CacheNode cache_node = FTC_LIST_TO_CACHENODE( node ); FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( cache_node ); FTC_Cache cache; FT_ListNode prev = node->prev; if ( data->ref_count <= 0 ) { /* ok, we are going to remove this node */ FT_List_Remove( &manager->global_lru, node ); /* finalize cache node */ cache = manager->caches[data->cache_index]; if ( cache ) { FTC_CacheNode_Class* clazz = cache->node_clazz; manager->num_bytes -= clazz->size_node( cache_node, cache->cache_data ); clazz->destroy_node( cache_node, cache->cache_data ); } else { /* this should never happen! */ FT_ERROR(( "FTC_Manager_Compress: Cache Manager is corrupted!\n" )); } /* check, just in case of general corruption :-) */ if ( manager->num_nodes <= 0 ) FT_ERROR(( "FTC_Manager_Compress: Invalid cache node count!\n" )); else manager->num_nodes--; } node = prev; } } FT_EXPORT( FT_Error ) FTC_Manager_Register_Cache( FTC_Manager manager, FTC_Cache_Class* clazz, FTC_Cache *acache ) { FT_Error error = FT_Err_Invalid_Argument; if ( manager && clazz && acache ) { FT_Memory memory = manager->library->memory; FTC_Cache cache; FT_UInt index = 0; /* by default, return 0 */ *acache = 0; /* check for an empty cache slot in the manager's table */ for ( index = 0; index < FTC_MAX_CACHES; index++ ) { if ( manager->caches[index] == 0 ) break; } /* return an error if there are too many registered caches */ if ( index >= FTC_MAX_CACHES ) { error = FT_Err_Too_Many_Caches; FT_ERROR(( "FTC_Manager_Register_Cache:" )); FT_ERROR(( " too many registered caches\n" )); goto Exit; } if ( !ALLOC( cache, clazz->cache_byte_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 = index; if ( clazz->init_cache ) error = clazz->init_cache( cache ); if ( error ) FREE( cache ); else manager->caches[index] = *acache = cache; } } Exit: return error; } /* END */