f546bacdcf
* src/cache/*, include/freetype/cache/*: fixing a bug after heavy testing. The current sources are now "release candidates" for the final version of the cache sub-system * Jamfile: updating "refdoc" target, and adding "autohint" to the list of modules to build. Both the autohinter and autofitter will be built by default. But which one will be used is determined by the content of "ftmodule.h" * src/autofit/*: much updates, but the code is still buggy as hell. Aargh..
646 lines
16 KiB
C
646 lines
16 KiB
C
/***************************************************************************/
|
|
/* */
|
|
/* ftcmanag.c */
|
|
/* */
|
|
/* FreeType Cache Manager (body). */
|
|
/* */
|
|
/* Copyright 2000-2001, 2002, 2003, 2004 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_MANAGER_H
|
|
#include FT_INTERNAL_OBJECTS_H
|
|
#include FT_INTERNAL_DEBUG_H
|
|
#include FT_SIZES_H
|
|
|
|
#include "ftcerror.h"
|
|
|
|
|
|
#undef FT_COMPONENT
|
|
#define FT_COMPONENT trace_cache
|
|
|
|
#define FTC_LRU_GET_MANAGER( lru ) ( (FTC_Manager)(lru)->user_data )
|
|
|
|
|
|
static FT_Error
|
|
ftc_scaler_lookup_size( FTC_Manager manager,
|
|
FTC_Scaler scaler,
|
|
FT_Size *asize )
|
|
{
|
|
FT_Face face;
|
|
FT_Size size = NULL;
|
|
FT_Error error;
|
|
|
|
|
|
error = FTC_Manager_LookupFace( manager, scaler->face_id, &face );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
error = FT_New_Size( face, &size );
|
|
if ( error )
|
|
goto Exit;
|
|
|
|
FT_Activate_Size( size );
|
|
|
|
if ( scaler->pixel )
|
|
error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height );
|
|
else
|
|
error = FT_Set_Char_Size( face, scaler->width, scaler->height,
|
|
scaler->x_res, scaler->y_res );
|
|
if ( error )
|
|
{
|
|
FT_Done_Size( size );
|
|
size = NULL;
|
|
}
|
|
|
|
Exit:
|
|
*asize = size;
|
|
return error;
|
|
}
|
|
|
|
|
|
typedef struct FTC_SizeNodeRec_
|
|
{
|
|
FTC_MruNodeRec node;
|
|
FT_Size size;
|
|
FTC_ScalerRec scaler;
|
|
|
|
} FTC_SizeNodeRec, *FTC_SizeNode;
|
|
|
|
|
|
FT_CALLBACK_DEF( void )
|
|
ftc_size_node_done( FTC_SizeNode node )
|
|
{
|
|
FT_Size size = node->size;
|
|
|
|
|
|
if ( size )
|
|
FT_Done_Size( size );
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Bool )
|
|
ftc_size_node_compare( FTC_SizeNode node,
|
|
FTC_Scaler scaler )
|
|
{
|
|
FTC_Scaler scaler0 = &node->scaler;
|
|
|
|
|
|
if ( FTC_SCALER_COMPARE( scaler0, scaler ) )
|
|
{
|
|
FT_Activate_Size( node->size );
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
ftc_size_node_init( FTC_SizeNode node,
|
|
FTC_Scaler scaler,
|
|
FTC_Manager manager )
|
|
{
|
|
node->scaler = scaler[0];
|
|
|
|
return ftc_scaler_lookup_size( manager, scaler, &node->size );
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
ftc_size_node_reset( FTC_SizeNode node,
|
|
FTC_Scaler scaler,
|
|
FTC_Manager manager )
|
|
{
|
|
FT_Done_Size( node->size );
|
|
|
|
node->scaler = scaler[0];
|
|
|
|
return ftc_scaler_lookup_size( manager, scaler, &node->size );
|
|
}
|
|
|
|
|
|
static const FTC_MruListClassRec ftc_size_list_class =
|
|
{
|
|
sizeof( FTC_SizeNodeRec ),
|
|
(FTC_MruNode_CompareFunc)ftc_size_node_compare,
|
|
(FTC_MruNode_InitFunc) ftc_size_node_init,
|
|
(FTC_MruNode_ResetFunc) ftc_size_node_reset,
|
|
(FTC_MruNode_DoneFunc) ftc_size_node_done
|
|
};
|
|
|
|
|
|
/* helper function used by ftc_face_node_done */
|
|
static FT_Bool
|
|
ftc_size_node_compare_faceid( FTC_SizeNode node,
|
|
FTC_FaceID face_id )
|
|
{
|
|
return FT_BOOL( node->scaler.face_id == face_id );
|
|
}
|
|
|
|
|
|
FT_EXPORT_DEF( FT_Error )
|
|
FTC_Manager_LookupSize( FTC_Manager manager,
|
|
FTC_Scaler scaler,
|
|
FT_Size *asize )
|
|
{
|
|
FT_Error error;
|
|
FTC_SizeNode node;
|
|
|
|
|
|
if ( asize == NULL )
|
|
return FTC_Err_Bad_Argument;
|
|
|
|
*asize = NULL;
|
|
|
|
if ( !manager )
|
|
return FTC_Err_Invalid_Cache_Handle;
|
|
|
|
#ifdef FTC_INLINE
|
|
|
|
FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare,
|
|
node, error );
|
|
|
|
#else
|
|
error = FTC_MruList_Lookup( &manager->sizes, scaler, (FTC_MruNode*)&node );
|
|
#endif
|
|
|
|
if ( !error )
|
|
*asize = node->size;
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** FACE MRU IMPLEMENTATION *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
typedef struct FTC_FaceNodeRec_
|
|
{
|
|
FTC_MruNodeRec node;
|
|
FTC_FaceID face_id;
|
|
FT_Face face;
|
|
|
|
} FTC_FaceNodeRec, *FTC_FaceNode;
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Error )
|
|
ftc_face_node_init( FTC_FaceNode node,
|
|
FTC_FaceID face_id,
|
|
FTC_Manager manager )
|
|
{
|
|
FT_Error error;
|
|
|
|
|
|
node->face_id = face_id;
|
|
|
|
error = manager->request_face( face_id,
|
|
manager->library,
|
|
manager->request_data,
|
|
&node->face );
|
|
if ( !error )
|
|
{
|
|
/* destroy initial size object; it will be re-created later */
|
|
if ( node->face->size )
|
|
FT_Done_Size( node->face->size );
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( void )
|
|
ftc_face_node_done( FTC_FaceNode node,
|
|
FTC_Manager manager )
|
|
{
|
|
/* we must begin by removing all scalers for the target face */
|
|
/* from the manager's list */
|
|
FTC_MruList_RemoveSelection(
|
|
& manager->sizes,
|
|
(FTC_MruNode_CompareFunc)ftc_size_node_compare_faceid,
|
|
node->face_id );
|
|
|
|
/* all right, we can discard the face now */
|
|
FT_Done_Face( node->face );
|
|
node->face = NULL;
|
|
node->face_id = NULL;
|
|
}
|
|
|
|
|
|
FT_CALLBACK_DEF( FT_Bool )
|
|
ftc_face_node_compare( FTC_FaceNode node,
|
|
FTC_FaceID face_id )
|
|
{
|
|
return FT_BOOL( node->face_id == face_id );
|
|
}
|
|
|
|
|
|
static const FTC_MruListClassRec ftc_face_list_class =
|
|
{
|
|
sizeof( FTC_FaceNodeRec),
|
|
|
|
(FTC_MruNode_CompareFunc)ftc_face_node_compare,
|
|
(FTC_MruNode_InitFunc) ftc_face_node_init,
|
|
(FTC_MruNode_ResetFunc) NULL,
|
|
(FTC_MruNode_DoneFunc) ftc_face_node_done
|
|
};
|
|
|
|
|
|
/* documentation is in ftcache.h */
|
|
|
|
FT_EXPORT_DEF( FT_Error )
|
|
FTC_Manager_LookupFace( FTC_Manager manager,
|
|
FTC_FaceID face_id,
|
|
FT_Face *aface )
|
|
{
|
|
FT_Error error;
|
|
FTC_FaceNode node;
|
|
|
|
|
|
if ( aface == NULL )
|
|
return FTC_Err_Bad_Argument;
|
|
|
|
*aface = NULL;
|
|
|
|
if ( !manager )
|
|
return FTC_Err_Invalid_Cache_Handle;
|
|
|
|
/* we break encapsulation for the sake of speed */
|
|
#ifdef FTC_INLINE
|
|
|
|
FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare,
|
|
node, error );
|
|
|
|
#else
|
|
error = FTC_MruList_Lookup( &manager->faces, face_id, (FTC_MruNode*)&node );
|
|
#endif
|
|
|
|
if ( !error )
|
|
*aface = node->face;
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
/***** *****/
|
|
/***** CACHE MANAGER ROUTINES *****/
|
|
/***** *****/
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
/* documentation is in ftcache.h */
|
|
|
|
FT_EXPORT_DEF( FT_Error )
|
|
FTC_Manager_New( FT_Library library,
|
|
FT_UInt max_faces,
|
|
FT_UInt max_sizes,
|
|
FT_ULong max_bytes,
|
|
FTC_Face_Requester requester,
|
|
FT_Pointer req_data,
|
|
FTC_Manager *amanager )
|
|
{
|
|
FT_Error error;
|
|
FT_Memory memory;
|
|
FTC_Manager manager = 0;
|
|
|
|
|
|
if ( !library )
|
|
return FTC_Err_Invalid_Library_Handle;
|
|
|
|
memory = library->memory;
|
|
|
|
if ( FT_NEW( manager ) )
|
|
goto Exit;
|
|
|
|
if ( max_faces == 0 )
|
|
max_faces = FTC_MAX_FACES_DEFAULT;
|
|
|
|
if ( max_sizes == 0 )
|
|
max_sizes = FTC_MAX_SIZES_DEFAULT;
|
|
|
|
if ( max_bytes == 0 )
|
|
max_bytes = FTC_MAX_BYTES_DEFAULT;
|
|
|
|
manager->library = library;
|
|
manager->memory = memory;
|
|
manager->max_weight = max_bytes;
|
|
|
|
manager->request_face = requester;
|
|
manager->request_data = req_data;
|
|
|
|
FTC_MruList_Init( &manager->faces,
|
|
&ftc_face_list_class,
|
|
max_faces,
|
|
manager,
|
|
memory );
|
|
|
|
FTC_MruList_Init( &manager->sizes,
|
|
&ftc_size_list_class,
|
|
max_sizes,
|
|
manager,
|
|
memory );
|
|
|
|
*amanager = manager;
|
|
|
|
Exit:
|
|
return error;
|
|
}
|
|
|
|
|
|
/* documentation is in ftcache.h */
|
|
|
|
FT_EXPORT_DEF( void )
|
|
FTC_Manager_Done( FTC_Manager manager )
|
|
{
|
|
FT_Memory memory;
|
|
FT_UInt idx;
|
|
|
|
|
|
if ( !manager || !manager->library )
|
|
return;
|
|
|
|
memory = manager->memory;
|
|
|
|
/* now discard all caches */
|
|
for (idx = manager->num_caches; idx-- > 0; )
|
|
{
|
|
FTC_Cache cache = manager->caches[idx];
|
|
|
|
|
|
if ( cache )
|
|
{
|
|
cache->clazz.cache_done( cache );
|
|
FT_FREE( cache );
|
|
manager->caches[idx] = NULL;
|
|
}
|
|
}
|
|
manager->num_caches = 0;
|
|
|
|
/* discard faces and sizes */
|
|
FTC_MruList_Done( &manager->sizes );
|
|
FTC_MruList_Done( &manager->faces );
|
|
|
|
manager->library = NULL;
|
|
manager->memory = NULL;
|
|
|
|
FT_FREE( manager );
|
|
}
|
|
|
|
|
|
/* documentation is in ftcache.h */
|
|
|
|
FT_EXPORT_DEF( void )
|
|
FTC_Manager_Reset( FTC_Manager manager )
|
|
{
|
|
if ( manager )
|
|
{
|
|
FTC_MruList_Reset( &manager->sizes );
|
|
FTC_MruList_Reset( &manager->faces );
|
|
}
|
|
/* XXX: FIXME: flush the caches? */
|
|
}
|
|
|
|
|
|
#ifdef FT_DEBUG_ERROR
|
|
|
|
FT_EXPORT_DEF( void )
|
|
FTC_Manager_Check( FTC_Manager manager )
|
|
{
|
|
FTC_Node node, first;
|
|
|
|
|
|
first = manager->nodes_list;
|
|
|
|
/* check node weights */
|
|
if ( first )
|
|
{
|
|
FT_ULong weight = 0;
|
|
|
|
|
|
node = first;
|
|
|
|
do
|
|
{
|
|
FTC_Cache cache = manager->caches[node->cache_index];
|
|
|
|
|
|
if ( (FT_UInt)node->cache_index >= manager->num_caches )
|
|
FT_ERROR(( "FTC_Manager_Check: invalid node (cache index = %ld\n",
|
|
node->cache_index ));
|
|
else
|
|
weight += cache->clazz.node_weight( node, cache );
|
|
|
|
node = FTC_NODE__NEXT( node );
|
|
|
|
} while ( node != first );
|
|
|
|
if ( weight != manager->cur_weight )
|
|
FT_ERROR(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
|
|
manager->cur_weight, weight ));
|
|
}
|
|
|
|
/* check circular list */
|
|
if ( first )
|
|
{
|
|
FT_UFast count = 0;
|
|
|
|
|
|
node = first;
|
|
do
|
|
{
|
|
count++;
|
|
node = FTC_NODE__NEXT( node );
|
|
|
|
} while ( node != first );
|
|
|
|
if ( count != manager->num_nodes )
|
|
FT_ERROR((
|
|
"FTC_Manager_Check: invalid cache node count %d instead of %d\n",
|
|
manager->num_nodes, count ));
|
|
}
|
|
}
|
|
|
|
#endif /* FT_DEBUG_ERROR */
|
|
|
|
|
|
/* `Compress' the manager's data, i.e., get rid of old cache nodes */
|
|
/* that are not referenced anymore in order to limit the total */
|
|
/* memory used by the cache. */
|
|
|
|
/* documentation is in ftcmanag.h */
|
|
|
|
FT_EXPORT_DEF( void )
|
|
FTC_Manager_Compress( FTC_Manager manager )
|
|
{
|
|
FTC_Node node, first;
|
|
|
|
|
|
if ( !manager )
|
|
return;
|
|
|
|
first = manager->nodes_list;
|
|
|
|
#ifdef FT_DEBUG_ERROR
|
|
FTC_Manager_Check( manager );
|
|
|
|
FT_ERROR(( "compressing, weight = %ld, max = %ld, nodes = %d\n",
|
|
manager->cur_weight, manager->max_weight,
|
|
manager->num_nodes ));
|
|
#endif
|
|
|
|
if ( manager->cur_weight < manager->max_weight || first == NULL )
|
|
return;
|
|
|
|
/* go to last node -- it's a circular list */
|
|
node = FTC_NODE__PREV( first );
|
|
do
|
|
{
|
|
FTC_Node prev;
|
|
|
|
|
|
prev = ( node == first ) ? NULL : FTC_NODE__PREV( node );
|
|
|
|
if ( node->ref_count <= 0 )
|
|
ftc_node_destroy( node, manager );
|
|
|
|
node = prev;
|
|
|
|
} while ( node && manager->cur_weight > manager->max_weight );
|
|
}
|
|
|
|
|
|
/* documentation is in ftcmanag.h */
|
|
|
|
FT_EXPORT_DEF( FT_Error )
|
|
FTC_Manager_RegisterCache( FTC_Manager manager,
|
|
FTC_CacheClass clazz,
|
|
FTC_Cache *acache )
|
|
{
|
|
FT_Error error = FTC_Err_Invalid_Argument;
|
|
FTC_Cache cache = NULL;
|
|
|
|
|
|
if ( manager && clazz && acache )
|
|
{
|
|
FT_Memory memory = manager->memory;
|
|
|
|
|
|
if ( manager->num_caches >= FTC_MAX_CACHES )
|
|
{
|
|
error = FTC_Err_Too_Many_Caches;
|
|
FT_ERROR(( "%s: too many registered caches\n",
|
|
"FTC_Manager_Register_Cache" ));
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !FT_ALLOC( cache, clazz->cache_size ) )
|
|
{
|
|
cache->manager = manager;
|
|
cache->memory = memory;
|
|
cache->clazz = clazz[0];
|
|
cache->org_class = clazz;
|
|
|
|
/* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */
|
|
/* IF IT IS NOT SET CORRECTLY */
|
|
cache->index = manager->num_caches;
|
|
|
|
error = clazz->cache_init( cache );
|
|
if ( error )
|
|
{
|
|
clazz->cache_done( cache );
|
|
FT_FREE( cache );
|
|
goto Exit;
|
|
}
|
|
|
|
manager->caches[manager->num_caches++] = cache;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
*acache = cache;
|
|
return error;
|
|
}
|
|
|
|
|
|
FT_EXPORT_DEF( FT_UInt )
|
|
FTC_Manager_FlushN( FTC_Manager manager,
|
|
FT_UInt count )
|
|
{
|
|
FTC_Node first = manager->nodes_list;
|
|
FTC_Node node;
|
|
FT_UInt result;
|
|
|
|
|
|
/* try to remove `count' nodes from the list */
|
|
if ( first == NULL ) /* empty list! */
|
|
return 0;
|
|
|
|
/* go to last node - it's a circular list */
|
|
node = FTC_NODE__PREV(first);
|
|
for ( result = 0; result < count; )
|
|
{
|
|
FTC_Node prev = FTC_NODE__PREV( node );
|
|
|
|
|
|
/* don't touch locked nodes */
|
|
if ( node->ref_count <= 0 )
|
|
{
|
|
ftc_node_destroy( node, manager );
|
|
result++;
|
|
}
|
|
|
|
if ( prev == manager->nodes_list )
|
|
break;
|
|
|
|
node = prev;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
FT_EXPORT_DEF( void )
|
|
FTC_Manager_RemoveFaceID( FTC_Manager manager,
|
|
FTC_FaceID face_id )
|
|
{
|
|
FT_UInt nn;
|
|
|
|
/* this will remove all FTC_SizeNode that correspond to
|
|
* the face_id as well
|
|
*/
|
|
FTC_MruList_RemoveSelection( &manager->faces, NULL, face_id );
|
|
|
|
for ( nn = 0; nn < manager->num_caches; nn++ )
|
|
FTC_Cache_RemoveFaceID( manager->caches[nn], face_id );
|
|
}
|
|
|
|
|
|
/* documentation is in ftcmanag.h */
|
|
|
|
FT_EXPORT_DEF( void )
|
|
FTC_Node_Unref( FTC_Node node,
|
|
FTC_Manager manager )
|
|
{
|
|
if ( node && (FT_UInt)node->cache_index < manager->num_caches )
|
|
node->ref_count--;
|
|
}
|
|
|
|
|
|
/* END */
|