| /***************************************************************************/ |
| /* */ |
| /* 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 <freetype/cache/ftcchunk.h> |
| #include <freetype/fterrors.h> |
| #include <freetype/internal/ftobjs.h> |
| #include <freetype/internal/ftlist.h> |
| #include <freetype/fterrors.h> |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** GLYPH NODES *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| |
| /* create a new chunk node, setting its cache index and ref count */ |
| FT_EXPORT_FUNC( 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 = (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_FUNC( 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_FUNC( FT_ULong ) FTC_ChunkNode_Size( FTC_ChunkNode node ) |
| { |
| FTC_ChunkSet cset = node->cset; |
| |
| |
| return cset->clazz->size_node( node ); |
| } |
| |
| |
| FT_CPLUSPLUS( 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_FUNC( 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_FUNC( 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; |
| |
| 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_FUNC( FT_Error ) |
| FTC_ChunkSet_Lookup_Node( FTC_ChunkSet cset, |
| FT_UInt glyph_index, |
| FTC_ChunkNode *anode, |
| FT_UInt *aindex ) |
| { |
| 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 = FT_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; |
| *aindex = 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 ) |
| |
| |
| LOCAL_FUNC_X |
| 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 = node - lru->nodes; |
| node->root.data = cset; |
| } |
| |
| return error; |
| } |
| |
| |
| LOCAL_FUNC_X |
| 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 ); |
| } |
| |
| |
| LOCAL_FUNC_X |
| 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_CPLUSPLUS( 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_FUNC( FT_Error ) FTC_Chunk_Cache_Init( FTC_Chunk_Cache cache ) |
| { |
| FT_Memory memory = cache->root.memory; |
| FT_Error error; |
| |
| |
| /* set up root node_class to be used by manager */ |
| cache->root.node_clazz = |
| (FTC_CacheNode_Class*)&ftc_chunk_cache_node_class; |
| |
| 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_FUNC( void ) FTC_Chunk_Cache_Done( FTC_Chunk_Cache cache ) |
| { |
| /* discard glyph sets */ |
| FT_Lru_Done( cache->csets_lru ); |
| } |
| |
| |