ref: 2909475716950301bf1e098a0a2d175521ed6250
dir: /src/cache/ftcglyph.c/
/***************************************************************************/ /* */ /* ftcglyph.c */ /* */ /* FreeType Glyph Image (FT_Glyph) 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/ftcglyph.h> #include <freetype/fterrors.h> #include <freetype/internal/ftobjs.h> #include <freetype/internal/ftlist.h> #include <freetype/fterrors.h> /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GLYPH NODES *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* create a new glyph node, setting its cache index and ref count */ FT_EXPORT_FUNC( void ) FTC_GlyphNode_Init( FTC_GlyphNode node, FTC_GlyphSet gset, FT_UInt gindex ) { FTC_Glyph_Cache cache = gset->cache; FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( &node->root ); data->cache_index = (FT_UShort)cache->root.cache_index; data->ref_count = (FT_Short) 0; node->gset_index = (FT_UShort)gset->gset_index; node->glyph_index = (FT_UShort)gindex; } /* Important: This function is called from the cache manager to */ /* destroy a given cache node during `cache compression'. The */ /* second argument is always `cache.cache_data'. Thus be */ /* certain that the function FTC_Glyph_Cache_New() does indeed */ /* set its `cache_data' field correctly, otherwise bad things */ /* will happen! */ FT_EXPORT_FUNC( void ) FTC_GlyphNode_Destroy( FTC_GlyphNode node, FTC_Glyph_Cache cache ) { FT_LruNode gset_lru = cache->gsets_lru->nodes + node->gset_index; FTC_GlyphSet gset = (FTC_GlyphSet)gset_lru->root.data; FT_UInt hash = node->glyph_index % gset->hash_size; /* remove the node from its gset's bucket list */ { FTC_GlyphNode* pnode = gset->buckets + hash; FTC_GlyphNode cur; for (;;) { cur = *pnode; if (!cur) { /* that's very strange, this should not happen !! */ FT_ERROR(( "FTC_GlyphNode_Destroy:" " trying to delete an unlisted node !!!!" )); return; } if (cur == node) { *pnode = cur->gset_next; break; } pnode = &cur->gset_next; } } /* destroy the node */ gset->clazz->destroy_node( node, gset ); } /* Important: This function is called from the cache manager to */ /* size a given cache node during `cache compression'. The */ /* second argument is always `cache.user_data'. Thus be */ /* certain that the function FTC_Glyph_Cache_New() does indeed */ /* set its `user_data' field correctly, otherwise bad things */ /* will happen! */ FT_EXPORT_FUNC( FT_ULong ) FTC_GlyphNode_Size( FTC_GlyphNode node, FTC_Glyph_Cache cache ) { FT_LruNode gset_lru = cache->gsets_lru->nodes + node->gset_index; FTC_GlyphSet gset = (FTC_GlyphSet)gset_lru->root.data; return gset->clazz->size_node( node, gset ); } FT_CPLUSPLUS( const FTC_CacheNode_Class ) ftc_glyph_cache_node_class = { (FTC_CacheNode_SizeFunc) FTC_GlyphNode_Size, (FTC_CacheNode_DestroyFunc) FTC_GlyphNode_Destroy }; /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GLYPH SETS *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ FT_EXPORT_FUNC( FT_Error ) FTC_GlyphSet_New( FTC_Glyph_Cache cache, FT_Pointer type, FTC_GlyphSet *aset ) { FT_Error error; FT_Memory memory = cache->root.memory; FTC_Manager manager = cache->root.manager; FTC_GlyphSet gset = 0; FTC_Glyph_Cache_Class* gcache_class; FTC_GlyphSet_Class* clazz; gcache_class = (FTC_Glyph_Cache_Class*)cache->root.clazz; clazz = gcache_class->gset_class; *aset = 0; if ( ALLOC( gset, clazz->gset_byte_size ) ) goto Exit; gset->cache = cache; gset->manager = manager; gset->memory = memory; gset->hash_size = FTC_GSET_HASH_SIZE_DEFAULT; gset->clazz = clazz; /* allocate buckets table */ if ( ALLOC_ARRAY( gset->buckets, gset->hash_size, FTC_GlyphNode ) ) goto Exit; /* initialize gset by type if needed */ if ( clazz->init ) { error = clazz->init( gset, type ); if ( error ) goto Exit; } *aset = gset; Exit: if ( error && gset ) { FREE( gset->buckets ); FREE( gset ); } return error; } FT_EXPORT_FUNC( void ) FTC_GlyphSet_Destroy( FTC_GlyphSet gset ) { FTC_Glyph_Cache cache = gset->cache; FTC_Manager manager = cache->root.manager; FT_List glyphs_lru = &manager->global_lru; FTC_GlyphNode* bucket = gset->buckets; FTC_GlyphNode* bucket_limit = bucket + gset->hash_size; FT_Memory memory = cache->root.memory; FTC_GlyphSet_Class* clazz = gset->clazz; /* for each bucket, free the list of glyph nodes */ for ( ; bucket < bucket_limit; bucket++ ) { FTC_GlyphNode node = bucket[0]; FTC_GlyphNode next = 0; FT_ListNode lrunode; for ( ; node; node = next ) { next = node->gset_next; lrunode = FTC_GLYPHNODE_TO_LRUNODE( node ); manager->num_bytes -= clazz->size_node( node, gset ); manager->num_nodes --; FT_List_Remove( glyphs_lru, lrunode ); clazz->destroy_node( node, gset ); } bucket[0] = 0; } if ( clazz->done ) clazz->done( gset ); FREE( gset->buckets ); FREE( gset ); } FT_EXPORT_FUNC( FT_Error ) FTC_GlyphSet_Lookup_Node( FTC_GlyphSet gset, FT_UInt glyph_index, FTC_GlyphNode *anode ) { FTC_Glyph_Cache cache = gset->cache; FTC_Manager manager = cache->root.manager; FT_UInt hash_index = glyph_index % gset->hash_size; FTC_GlyphNode* bucket = gset->buckets + hash_index; FTC_GlyphNode* pnode = bucket; FTC_GlyphNode node; FT_Error error; FTC_GlyphSet_Class* clazz = gset->clazz; *anode = 0; for ( ;; ) { node = *pnode; if (!node) break; if ( node->glyph_index == glyph_index ) { /* we found it! -- move glyph to start of the lists */ *pnode = node->gset_next; node->gset_next = bucket[0]; bucket[0] = node; FT_List_Up( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) ); *anode = node; return 0; } /* go to next node in bucket */ pnode = &node->gset_next; } /* we didn't found the glyph image, we will now create a new one */ error = clazz->new_node( gset, glyph_index, &node ); if ( error ) goto Exit; /* insert the node at the start of our bucket list */ node->gset_next = bucket[0]; bucket[0] = node; /* insert the node at the start the global LRU glyph list */ FT_List_Insert( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) ); manager->num_bytes += clazz->size_node( node, gset ); manager->num_nodes ++; if (manager->num_bytes > manager->max_bytes) { FTC_GlyphNode_Ref ( node ); FTC_Manager_Compress( manager ); FTC_GlyphNode_Unref ( node ); } *anode = node; Exit: return error; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GLYPH SETS LRU CALLBACKS *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ #define FTC_GSET_LRU_GET_CACHE( lru ) \ ( (FTC_Glyph_Cache)(lru)->user_data ) #define FTC_GSET_LRU_GET_MANAGER( lru ) \ FTC_GSET_LRU_GET_CACHE( lru )->manager #define FTC_LRUNODE_GSET( node ) \ ( (FTC_GlyphSet)(node)->root.data ) LOCAL_FUNC_X FT_Error ftc_glyph_set_lru_init( FT_Lru lru, FT_LruNode node ) { FTC_Glyph_Cache cache = FTC_GSET_LRU_GET_CACHE( lru ); FT_Error error; FTC_GlyphSet gset; error = FTC_GlyphSet_New( cache, (FT_Pointer)node->key, &gset ); if ( !error ) { /* good, now set the gset index within the gset object */ gset->gset_index = node - lru->nodes; node->root.data = gset; } return error; } LOCAL_FUNC_X void ftc_glyph_set_lru_done( FT_Lru lru, FT_LruNode node ) { FTC_GlyphSet gset = FTC_LRUNODE_GSET( node ); FT_UNUSED( lru ); FTC_GlyphSet_Destroy( gset ); } LOCAL_FUNC_X FT_Bool ftc_glyph_set_lru_compare( FT_LruNode node, FT_LruKey key ) { FTC_GlyphSet gset = FTC_LRUNODE_GSET( node ); return gset->clazz->compare( gset, (FT_Pointer)key ); } FT_CPLUSPLUS( const FT_Lru_Class ) ftc_glyph_set_lru_class = { sizeof( FT_LruRec ), ftc_glyph_set_lru_init, ftc_glyph_set_lru_done, 0, /* no flush */ ftc_glyph_set_lru_compare }; /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GLYPH CACHE OBJECTS *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ FT_EXPORT_FUNC( FT_Error ) FTC_Glyph_Cache_Init( FTC_Glyph_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_glyph_cache_node_class; /* The following is extremely important for ftc_destroy_glyph_image() */ /* to work properly, as the second parameter that is sent to it */ /* through the cache manager is `cache_data' and must be set to */ /* `cache' here. */ /* */ cache->root.cache_data = cache; error = FT_Lru_New( &ftc_glyph_set_lru_class, FTC_MAX_GLYPH_SETS, cache, memory, 1, /* pre_alloc == TRUE */ &cache->gsets_lru ); return error; } FT_EXPORT_FUNC( void ) FTC_Glyph_Cache_Done( FTC_Glyph_Cache cache ) { /* discard glyph sets */ FT_Lru_Done( cache->gsets_lru ); } /* END */