freetype/src/cache/ftcglyph.c

421 lines
14 KiB
C
Raw Normal View History

/***************************************************************************/
/* */
/* ftcglyph.c */
/* */
2000-10-12 07:05:40 +02:00
/* 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. */
/* */
/***************************************************************************/
2000-10-12 07:05:40 +02:00
/*************************************************************************/
/* */
/* Note: The implementation of glyph queues is rather generic in this */
/* code. This will allow other glyph node/cache types to be */
/* easily included in the future. For now, we only cache glyph */
/* images. */
/* */
/*************************************************************************/
#include <freetype/cache/ftcglyph.h>
#include <freetype/fterrors.h>
#include <freetype/internal/ftobjs.h>
#include <freetype/internal/ftlist.h>
#include <freetype/fterrors.h>
2000-10-12 07:05:40 +02:00
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GLYPH NODES *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* In the future, we might provide a better scheme for managing glyph */
/* node elements. For the moment, we simply use FT_Alloc()/FT_Free(). */
/* create a new glyph node, setting its cache index and ref count */
FT_EXPORT_FUNC( void ) FTC_GlyphNode_Init( FTC_GlyphNode node,
FTC_Glyph_Queue queue,
FT_UInt gindex )
{
FTC_Glyph_Cache cache = queue->cache;
FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( &node->root );
2000-10-12 07:05:40 +02:00
data->cache_index = (FT_UShort)cache->root.cache_index;
data->ref_count = (FT_Short) 0;
node->queue_index = (FT_UShort)queue->queue_index;
node->glyph_index = (FT_UShort)gindex;
}
2000-10-12 07:05:40 +02:00
/* Important: This function is called from the cache manager to */
/* destroy 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 */
2000-10-12 07:05:40 +02:00
/* set its `user_data' field correctly, otherwise bad things */
/* will happen! */
2000-10-12 07:05:40 +02:00
FT_EXPORT_FUNC( void ) FTC_GlyphNode_Destroy( FTC_GlyphNode node,
FTC_Glyph_Cache cache )
{
2000-10-12 07:05:40 +02:00
FT_LruNode queue_lru = cache->queues_lru->nodes + node->queue_index;
FTC_Glyph_Queue queue = (FTC_Glyph_Queue)queue_lru->root.data;
FT_UInt hash = node->glyph_index % queue->hash_size;
/* remove the node from its queue's bucket list */
{
FTC_GlyphNode* pnode = queue->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->queue_next;
break;
}
pnode = &cur->queue_next;
}
}
2000-10-12 07:05:40 +02:00
/* destroy the node */
queue->clazz->destroy_node( node, queue );
}
2000-10-12 07:05:40 +02:00
/* 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 */
2000-10-12 07:05:40 +02:00
/* 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 )
{
2000-10-12 07:05:40 +02:00
FT_LruNode queue_lru = cache->queues_lru->nodes + node->queue_index;
FTC_Glyph_Queue queue = (FTC_Glyph_Queue)queue_lru->root.data;
2000-10-12 07:05:40 +02:00
return queue->clazz->size_node( node, queue );
}
2000-10-12 07:05:40 +02:00
FT_CPLUSPLUS( const FTC_CacheNode_Class ) ftc_glyph_cache_node_class =
{
2000-10-12 07:05:40 +02:00
(FTC_CacheNode_SizeFunc) FTC_GlyphNode_Size,
(FTC_CacheNode_DestroyFunc)FTC_GlyphNode_Destroy
};
2000-10-12 07:05:40 +02:00
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GLYPH QUEUES *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
FT_EXPORT_FUNC( FT_Error ) FTC_Glyph_Queue_New( FTC_Glyph_Cache cache,
FT_Pointer type,
FTC_Glyph_Queue* aqueue )
{
FT_Error error;
FT_Memory memory = cache->root.memory;
FTC_Manager manager = cache->root.manager;
FTC_Glyph_Queue queue = 0;
2000-10-12 07:05:40 +02:00
FTC_Glyph_Cache_Class* gcache_class;
FTC_Glyph_Queue_Class* clazz;
gcache_class = (FTC_Glyph_Cache_Class*)cache->root.clazz;
clazz = gcache_class->queue_class;
2000-10-12 07:05:40 +02:00
*aqueue = 0;
2000-10-12 07:05:40 +02:00
if ( ALLOC( queue, clazz->queue_byte_size ) )
goto Exit;
2000-10-12 07:05:40 +02:00
queue->cache = cache;
queue->manager = manager;
queue->memory = memory;
queue->hash_size = FTC_QUEUE_HASH_SIZE_DEFAULT;
queue->clazz = clazz;
/* allocate buckets table */
if ( ALLOC_ARRAY( queue->buckets, queue->hash_size, FTC_GlyphNode ) )
goto Exit;
2000-10-12 07:05:40 +02:00
/* initialize queue by type if needed */
if ( clazz->init )
{
error = clazz->init( queue, type );
2000-10-12 07:05:40 +02:00
if ( error )
goto Exit;
}
*aqueue = queue;
Exit:
if ( error && queue )
{
FREE( queue->buckets );
FREE( queue );
}
return error;
2000-10-12 07:05:40 +02:00
}
2000-10-12 07:05:40 +02:00
FT_EXPORT_FUNC( void ) FTC_Glyph_Queue_Done( FTC_Glyph_Queue queue )
{
2000-10-12 07:05:40 +02:00
FTC_Glyph_Cache cache = queue->cache;
FTC_Manager manager = cache->root.manager;
FT_List glyphs_lru = &manager->global_lru;
FTC_GlyphNode* bucket = queue->buckets;
FTC_GlyphNode* bucket_limit = bucket + queue->hash_size;
2000-10-12 07:05:40 +02:00
FT_Memory memory = cache->root.memory;
FTC_Glyph_Queue_Class* clazz = queue->clazz;
2000-10-12 07:05:40 +02:00
/* 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;
2000-10-12 07:05:40 +02:00
for ( ; node; node = next )
{
next = node->queue_next;
lrunode = FTC_GLYPHNODE_TO_LRUNODE( node );
2000-10-12 07:05:40 +02:00
manager->num_bytes -= clazz->size_node( node, queue );
2000-10-12 07:05:40 +02:00
FT_List_Remove( glyphs_lru, lrunode );
clazz->destroy_node( node, queue );
}
2000-10-12 07:05:40 +02:00
bucket[0] = 0;
}
2000-10-12 07:05:40 +02:00
if ( clazz->done )
clazz->done( queue );
FREE( queue->buckets );
FREE( queue );
}
FT_EXPORT_FUNC( FT_Error )
FTC_Glyph_Queue_Lookup_Node( FTC_Glyph_Queue queue,
FT_UInt glyph_index,
FTC_GlyphNode* anode )
{
2000-10-12 07:05:40 +02:00
FTC_Glyph_Cache cache = queue->cache;
FTC_Manager manager = cache->root.manager;
FT_UInt hash_index = glyph_index % queue->hash_size;
FTC_GlyphNode* bucket = queue->buckets + hash_index;
FTC_GlyphNode* pnode = bucket;
FTC_GlyphNode node;
2000-10-12 07:05:40 +02:00
FT_Error error;
FTC_Glyph_Queue_Class* clazz = queue->clazz;
2000-10-12 07:05:40 +02:00
*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->queue_next;
node->queue_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->queue_next;
}
/* we didn't found the glyph image, we will now create a new one */
error = clazz->new_node( queue, glyph_index, &node );
if ( error )
goto Exit;
/* insert the node at the start of our bucket list */
node->queue_next = bucket[0];
bucket[0] = node;
2000-10-12 07:05:40 +02:00
/* insert the node at the start the global LRU glyph list */
FT_List_Insert( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );
2000-10-12 07:05:40 +02:00
manager->num_bytes += clazz->size_node( node, queue );
if (manager->num_bytes > manager->max_bytes)
FTC_Manager_Compress( manager );
2000-10-12 07:05:40 +02:00
*anode = node;
Exit:
return error;
}
2000-10-12 07:05:40 +02:00
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GLYPH QUEUES LRU CALLBACKS *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
2000-10-12 07:05:40 +02:00
#define FTC_QUEUE_LRU_GET_CACHE( lru ) \
( (FTC_Glyph_Cache)(lru)->user_data )
2000-10-12 07:05:40 +02:00
#define FTC_QUEUE_LRU_GET_MANAGER( lru ) \
FTC_QUEUE_LRU_GET_CACHE( lru )->manager
2000-10-12 07:05:40 +02:00
#define FTC_LRUNODE_QUEUE( node ) \
( (FTC_Glyph_Queue)(node)->root.data )
LOCAL_FUNC_X
FT_Error ftc_glyph_queue_lru_init( FT_Lru lru,
FT_LruNode node )
{
FTC_Glyph_Cache cache = FTC_QUEUE_LRU_GET_CACHE( lru );
FT_Error error;
FTC_Glyph_Queue queue;
2000-10-12 07:05:40 +02:00
error = FTC_Glyph_Queue_New( cache,
(FT_Pointer)node->key,
&queue );
if ( !error )
{
/* good, now set the queue index within the queue object */
queue->queue_index = node - lru->nodes;
node->root.data = queue;
}
2000-10-12 07:05:40 +02:00
return error;
}
LOCAL_FUNC_X
void ftc_glyph_queue_lru_done( FT_Lru lru,
FT_LruNode node )
{
FTC_Glyph_Queue queue = FTC_LRUNODE_QUEUE( node );
2000-10-12 07:05:40 +02:00
FT_UNUSED( lru );
FTC_Glyph_Queue_Done( queue );
}
LOCAL_FUNC_X
FT_Bool ftc_glyph_queue_lru_compare( FT_LruNode node,
FT_LruKey key )
{
FTC_Glyph_Queue queue = FTC_LRUNODE_QUEUE( node );
2000-10-12 07:05:40 +02:00
return queue->clazz->compare( queue, (FT_Pointer)key );
}
FT_CPLUSPLUS( const FT_Lru_Class ) ftc_glyph_queue_lru_class =
{
sizeof( FT_LruRec ),
ftc_glyph_queue_lru_init,
ftc_glyph_queue_lru_done,
0, /* no flush */
ftc_glyph_queue_lru_compare
};
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GLYPH IMAGE CACHE OBJECTS *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
2000-10-12 07:05:40 +02:00
FT_EXPORT_FUNC( FT_Error ) FTC_Glyph_Cache_Init( FTC_Glyph_Cache cache )
{
2000-10-12 07:05:40 +02:00
FT_Memory memory = cache->root.memory;
FT_Error error;
/* set up root node_class to be used by manager */
2000-10-12 07:05:40 +02:00
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 `user_data' and must be set to */
/* `cache' here. */
/* */
cache->root.cache_user = cache;
error = FT_Lru_New( &ftc_glyph_queue_lru_class,
FTC_MAX_GLYPH_QUEUES,
cache,
memory,
1, /* pre_alloc == TRUE */
&cache->queues_lru );
return error;
}
2000-10-12 07:05:40 +02:00
FT_EXPORT_FUNC( void ) FTC_Glyph_Cache_Done( FTC_Glyph_Cache cache )
{
2000-10-12 07:05:40 +02:00
/* discard glyph queues */
FT_Lru_Done( cache->queues_lru );
}
/* END */