| /***************************************************************************/ |
| /* */ |
| /* ftcchunk.c */ |
| /* */ |
| /* FreeType chunk cache cache (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 <ft2build.h> |
| #include FT_CACHE_H |
| #include FT_CACHE_INTERNAL_CHUNK_H |
| #include FT_LIST_H |
| #include FT_ERRORS_H |
| #include FT_INTERNAL_OBJECTS_H |
| |
| #include "ftcerror.h" |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** GLYPH NODES *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| /* create a new chunk node, setting its cache index and ref count */ |
| FT_EXPORT_DEF( FT_Error ) FTC_ChunkNode_Init( FTC_ChunkNode node, |
| FTC_ChunkSet cset, |
| FT_UInt index, |
| FT_Bool alloc ) |
| { |
| FTC_Chunk_Cache cache = cset->cache; |
| FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( &node->root ); |
| FT_Error error = 0; |
| |
| |
| data->cache_index = (FT_UShort)cache->root.cache_index; |
| data->ref_count = (FT_Short) 0; |
| node->cset = cset; |
| node->cset_index = (FT_UShort)index; |
| node->num_elements = (unsigned short)( |
| ( index + 1 < cset->num_chunks ) |
| ? cset->element_count |
| : cset->element_max - cset->element_count*index ); |
| if ( alloc ) |
| { |
| /* allocate elements array */ |
| FT_Memory memory; |
| |
| |
| memory = cache->root.memory; |
| error = MEM_Alloc( node->elements, |
| cset->element_size * cset->element_count ); |
| } |
| |
| return error; |
| } |
| |
| |
| FT_EXPORT_DEF( void ) FTC_ChunkNode_Destroy( FTC_ChunkNode node ) |
| { |
| FTC_ChunkSet cset = node->cset; |
| |
| |
| /* remove from parent set table */ |
| cset->chunks[node->cset_index] = 0; |
| |
| /* destroy the node */ |
| cset->clazz->destroy_node( node ); |
| } |
| |
| |
| FT_EXPORT_DEF( FT_ULong ) FTC_ChunkNode_Size( FTC_ChunkNode node ) |
| { |
| FTC_ChunkSet cset = node->cset; |
| |
| |
| return cset->clazz->size_node( node ); |
| } |
| |
| |
| FT_CALLBACK_TABLE_DEF |
| const FTC_CacheNode_Class ftc_chunk_cache_node_class = |
| { |
| (FTC_CacheNode_SizeFunc) FTC_ChunkNode_Size, |
| (FTC_CacheNode_DestroyFunc)FTC_ChunkNode_Destroy |
| }; |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** CHUNK SETS *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| FT_EXPORT_DEF( FT_Error ) FTC_ChunkSet_New( FTC_Chunk_Cache cache, |
| FT_Pointer type, |
| FTC_ChunkSet *aset ) |
| { |
| FT_Error error; |
| FT_Memory memory = cache->root.memory; |
| FTC_Manager manager = cache->root.manager; |
| FTC_ChunkSet cset = 0; |
| |
| FTC_Chunk_Cache_Class* ccache_class; |
| FTC_ChunkSet_Class* clazz; |
| |
| |
| ccache_class = (FTC_Chunk_Cache_Class*)cache->root.clazz; |
| clazz = ccache_class->cset_class; |
| |
| *aset = 0; |
| |
| if ( ALLOC( cset, clazz->cset_byte_size ) ) |
| goto Exit; |
| |
| cset->cache = cache; |
| cset->manager = manager; |
| cset->memory = memory; |
| cset->clazz = clazz; |
| |
| /* now compute element_max, element_count and element_size */ |
| error = clazz->sizes( cset, type ); |
| if ( error ) |
| goto Exit; |
| |
| /* compute maximum number of nodes */ |
| cset->num_chunks = ( cset->element_max + cset->element_count - 1 ) / |
| cset->element_count; |
| |
| /* allocate chunk pointers table */ |
| if ( ALLOC_ARRAY( cset->chunks, cset->num_chunks, FTC_ChunkNode ) ) |
| goto Exit; |
| |
| /* initialize set by type if needed */ |
| if ( clazz->init ) |
| { |
| error = clazz->init( cset, type ); |
| if ( error ) |
| goto Exit; |
| } |
| |
| *aset = cset; |
| |
| Exit: |
| if ( error && cset ) |
| { |
| FREE( cset->chunks ); |
| FREE( cset ); |
| } |
| |
| return error; |
| } |
| |
| |
| FT_EXPORT_DEF( void ) FTC_ChunkSet_Destroy( FTC_ChunkSet cset ) |
| { |
| FTC_Chunk_Cache cache = cset->cache; |
| FTC_Manager manager = cache->root.manager; |
| FT_List glyphs_lru = &manager->global_lru; |
| FTC_ChunkNode* bucket = cset->chunks; |
| FTC_ChunkNode* bucket_limit = bucket + cset->num_chunks; |
| FT_Memory memory = cache->root.memory; |
| |
| FTC_ChunkSet_Class* clazz = cset->clazz; |
| |
| |
| /* for each bucket, free the list of glyph nodes */ |
| for ( ; bucket < bucket_limit; bucket++ ) |
| { |
| FTC_ChunkNode node = bucket[0]; |
| FT_ListNode lrunode; |
| |
| |
| if ( node ) |
| { |
| lrunode = FTC_CHUNKNODE_TO_LRUNODE( node ); |
| |
| manager->num_bytes -= clazz->size_node( node ); |
| manager->num_nodes--; |
| |
| FT_List_Remove( glyphs_lru, lrunode ); |
| |
| clazz->destroy_node( node ); |
| |
| bucket[0] = 0; |
| } |
| } |
| |
| if ( clazz->done ) |
| clazz->done( cset ); |
| |
| FREE( cset->chunks ); |
| FREE( cset ); |
| } |
| |
| |
| FT_EXPORT_DEF( FT_Error ) FTC_ChunkSet_Lookup_Node( |
| FTC_ChunkSet cset, |
| FT_UInt glyph_index, |
| FTC_ChunkNode *anode, |
| FT_UInt *anindex ) |
| { |
| FTC_Chunk_Cache cache = cset->cache; |
| FTC_Manager manager = cache->root.manager; |
| FT_Error error = 0; |
| |
| FTC_ChunkSet_Class* clazz = cset->clazz; |
| |
| |
| *anode = 0; |
| |
| if ( glyph_index >= cset->element_max ) |
| error = FTC_Err_Invalid_Argument; |
| else |
| { |
| FT_UInt chunk_size = cset->element_count; |
| FT_UInt chunk_index = glyph_index / chunk_size; |
| FTC_ChunkNode* pnode = cset->chunks + chunk_index; |
| FTC_ChunkNode node = *pnode; |
| |
| |
| if ( !node ) |
| { |
| /* we didn't found the glyph image; we will now create a new one */ |
| error = clazz->new_node( cset, chunk_index, &node ); |
| if ( error ) |
| goto Exit; |
| |
| /* store the new chunk in the cset's table */ |
| *pnode = node; |
| |
| /* insert the node at the start the global LRU glyph list */ |
| FT_List_Insert( &manager->global_lru, |
| FTC_CHUNKNODE_TO_LRUNODE( node ) ); |
| |
| manager->num_bytes += clazz->size_node( node ); |
| manager->num_nodes++; |
| |
| if ( manager->num_bytes > manager->max_bytes ) |
| { |
| FTC_ChunkNode_Ref ( node ); |
| FTC_Manager_Compress( manager ); |
| FTC_ChunkNode_Unref ( node ); |
| } |
| } |
| |
| *anode = node; |
| *anindex = glyph_index - chunk_index * chunk_size; |
| } |
| |
| Exit: |
| return error; |
| } |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** CHUNK SETS LRU CALLBACKS *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| #define FTC_CSET_LRU_GET_CACHE( lru ) \ |
| ( (FTC_Chunk_Cache)((lru)->user_data) ) |
| |
| #define FTC_CSET_LRU_GET_MANAGER( lru ) \ |
| FTC_CSET_LRU_GET_CACHE( lru )->manager |
| |
| #define FTC_LRUNODE_CSET( node ) \ |
| ( (FTC_ChunkSet)(node)->root.data ) |
| |
| |
| FT_CALLBACK_DEF(FT_Error) |
| ftc_chunk_set_lru_init( FT_Lru lru, |
| FT_LruNode node ) |
| { |
| FTC_Chunk_Cache cache = FTC_CSET_LRU_GET_CACHE( lru ); |
| FT_Error error; |
| FTC_ChunkSet cset; |
| |
| |
| error = FTC_ChunkSet_New( cache, |
| (FT_Pointer)node->key, |
| &cset ); |
| if ( !error ) |
| { |
| /* good, now set the set index within the set object */ |
| cset->cset_index = (FT_UInt)( node - lru->nodes ); |
| node->root.data = cset; |
| } |
| |
| return error; |
| } |
| |
| |
| FT_CALLBACK_DEF(void) |
| ftc_chunk_set_lru_done( FT_Lru lru, |
| FT_LruNode node ) |
| { |
| FTC_ChunkSet cset = FTC_LRUNODE_CSET( node ); |
| |
| FT_UNUSED( lru ); |
| |
| |
| FTC_ChunkSet_Destroy( cset ); |
| } |
| |
| |
| FT_CALLBACK_DEF(FT_Bool) |
| ftc_chunk_set_lru_compare( FT_LruNode node, |
| FT_LruKey key ) |
| { |
| FTC_ChunkSet cset = FTC_LRUNODE_CSET( node ); |
| |
| |
| return cset->clazz->compare( cset, (FT_Pointer)key ); |
| } |
| |
| |
| FT_CALLBACK_TABLE_DEF |
| const FT_Lru_Class ftc_chunk_set_lru_class = |
| { |
| sizeof( FT_LruRec ), |
| ftc_chunk_set_lru_init, |
| ftc_chunk_set_lru_done, |
| 0, /* no flush */ |
| ftc_chunk_set_lru_compare |
| }; |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** CHUNK CACHE OBJECTS *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| FT_EXPORT_DEF( FT_Error ) FTC_Chunk_Cache_Init( FTC_Chunk_Cache cache ) |
| { |
| FT_Memory memory = cache->root.memory; |
| FT_Error error; |
| |
| FTC_Chunk_Cache_Class* ccache_clazz; |
| |
| |
| /* set up root node_class to be used by manager */ |
| cache->root.node_clazz = |
| (FTC_CacheNode_Class*)&ftc_chunk_cache_node_class; |
| |
| /* setup `compare' shortcut */ |
| ccache_clazz = (FTC_Chunk_Cache_Class*)cache->root.clazz; |
| cache->compare = ccache_clazz->cset_class->compare; |
| |
| error = FT_Lru_New( &ftc_chunk_set_lru_class, |
| FTC_MAX_CHUNK_SETS, |
| cache, |
| memory, |
| 1, /* pre_alloc == TRUE */ |
| &cache->csets_lru ); |
| return error; |
| } |
| |
| |
| FT_EXPORT_DEF( void ) FTC_Chunk_Cache_Done( FTC_Chunk_Cache cache ) |
| { |
| /* discard glyph sets */ |
| FT_Lru_Done( cache->csets_lru ); |
| } |
| |
| |
| FT_EXPORT_DEF( FT_Error ) FTC_Chunk_Cache_Lookup( FTC_Chunk_Cache cache, |
| FT_Pointer type, |
| FT_UInt gindex, |
| FTC_ChunkNode *anode, |
| FT_UInt *aindex ) |
| { |
| FT_Error error; |
| FTC_ChunkSet cset; |
| FTC_ChunkNode node; |
| FT_UInt cindex; |
| FTC_Manager manager; |
| |
| |
| /* check for valid `desc' delayed to FT_Lru_Lookup() */ |
| |
| if ( !cache || !anode || !aindex ) |
| return FTC_Err_Invalid_Argument; |
| |
| *anode = 0; |
| *aindex = 0; |
| cset = cache->last_cset; |
| |
| if ( !cset || !cache->compare( cset, type ) ) |
| { |
| error = FT_Lru_Lookup( cache->csets_lru, |
| (FT_LruKey)type, |
| (FT_Pointer*)&cset ); |
| cache->last_cset = cset; |
| if ( error ) |
| goto Exit; |
| } |
| |
| error = FTC_ChunkSet_Lookup_Node( cset, gindex, &node, &cindex ); |
| if ( error ) |
| goto Exit; |
| |
| /* now compress the manager's cache pool if needed */ |
| manager = cache->root.manager; |
| if ( manager->num_bytes > manager->max_bytes ) |
| { |
| FTC_ChunkNode_Ref ( node ); |
| FTC_Manager_Compress( manager ); |
| FTC_ChunkNode_Unref ( node ); |
| } |
| |
| *anode = node; |
| *aindex = cindex; |
| |
| Exit: |
| return error; |
| } |
| |
| |
| /* END */ |