/***************************************************************************/ /* */ /* 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 #include FT_CACHE_H #include FT_CACHE_INTERNAL_GLYPH_H #include FT_ERRORS_H #include FT_LIST_H #include FT_INTERNAL_OBJECTS_H #include FT_INTERNAL_DEBUG_H /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GLYPH NODES *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* create a new glyph node, setting its cache index and ref count */ FT_EXPORT_DEF( 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_DEF( 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 ) { /* this should never 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_DEF( 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_CALLBACK_TABLE_DEF 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_DEF( 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_DEF( 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_DEF( 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 ( (FT_UInt)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 ) FT_CALLBACK_DEF 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 = (FT_UInt)( node - lru->nodes ); node->root.data = gset; } return error; } FT_CALLBACK_DEF 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 ); } FT_CALLBACK_DEF 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_CALLBACK_TABLE_DEF 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_DEF( FT_Error ) FTC_Glyph_Cache_Init( FTC_Glyph_Cache cache ) { FT_Memory memory = cache->root.memory; FT_Error error; FTC_Glyph_Cache_Class* gcache_clazz; /* set up root node_class to be used by manager */ cache->root.node_clazz = (FTC_CacheNode_Class*)&ftc_glyph_cache_node_class; /* setup the `compare' shortcut */ gcache_clazz = (FTC_Glyph_Cache_Class*)cache->root.clazz; cache->compare = gcache_clazz->gset_class->compare; /* 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_DEF( void ) FTC_Glyph_Cache_Done( FTC_Glyph_Cache cache ) { /* discard glyph sets */ FT_Lru_Done( cache->gsets_lru ); } FT_EXPORT_DEF( FT_Error ) FTC_Glyph_Cache_Lookup( FTC_Glyph_Cache cache, FT_Pointer type, FT_UInt gindex, FTC_GlyphNode *anode ) { FT_Error error; FTC_GlyphSet gset; FTC_GlyphNode node; FTC_Manager manager; /* check for valid `desc' delayed to FT_Lru_Lookup() */ if ( !cache || !anode ) return FT_Err_Invalid_Argument; *anode = 0; gset = cache->last_gset; if ( !gset || !cache->compare( gset, type ) ) { error = FT_Lru_Lookup( cache->gsets_lru, (FT_LruKey)type, (FT_Pointer*)&gset ); cache->last_gset = gset; if ( error ) goto Exit; } error = FTC_GlyphSet_Lookup_Node( gset, gindex, &node ); 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_GlyphNode_Ref ( node ); FTC_Manager_Compress( manager ); FTC_GlyphNode_Unref ( node ); } *anode = node; Exit: return error; } /* END */