/***************************************************************************/ /* */ /* ftcbasic.c */ /* */ /* The FreeType basic cache interface (body). */ /* */ /* Copyright 2003, 2004, 2005, 2006, 2007, 2009, 2010 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_INTERNAL_DEBUG_H #include FT_CACHE_H #include "ftcglyph.h" #include "ftcimage.h" #include "ftcsbits.h" #include "ftccback.h" #include "ftcerror.h" #define FT_COMPONENT trace_cache #ifdef FT_CONFIG_OPTION_OLD_INTERNALS /* * These structures correspond to the FTC_Font and FTC_ImageDesc types * that were defined in version 2.1.7. */ typedef struct FTC_OldFontRec_ { FTC_FaceID face_id; FT_UShort pix_width; FT_UShort pix_height; } FTC_OldFontRec, *FTC_OldFont; typedef struct FTC_OldImageDescRec_ { FTC_OldFontRec font; FT_UInt32 flags; } FTC_OldImageDescRec, *FTC_OldImageDesc; /* * Notice that FTC_OldImageDescRec and FTC_ImageTypeRec are nearly * identical, bit-wise. The only difference is that the `width' and * `height' fields are expressed as 16-bit integers in the old structure, * and as normal `int' in the new one. * * We are going to perform a weird hack to detect which structure is * being passed to the image and sbit caches. If the new structure's * `width' is larger than 0x10000, we assume that we are really receiving * an FTC_OldImageDesc. */ #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */ /* * Basic Families * */ typedef struct FTC_BasicAttrRec_ { FTC_ScalerRec scaler; FT_UInt load_flags; } FTC_BasicAttrRec, *FTC_BasicAttrs; #define FTC_BASIC_ATTR_COMPARE( a, b ) \ FT_BOOL( FTC_SCALER_COMPARE( &(a)->scaler, &(b)->scaler ) && \ (a)->load_flags == (b)->load_flags ) #define FTC_BASIC_ATTR_HASH( a ) \ ( FTC_SCALER_HASH( &(a)->scaler ) + 31*(a)->load_flags ) typedef struct FTC_BasicQueryRec_ { FTC_GQueryRec gquery; FTC_BasicAttrRec attrs; } FTC_BasicQueryRec, *FTC_BasicQuery; typedef struct FTC_BasicFamilyRec_ { FTC_FamilyRec family; FTC_BasicAttrRec attrs; } FTC_BasicFamilyRec, *FTC_BasicFamily; FT_CALLBACK_DEF( FT_Bool ) ftc_basic_family_compare( FTC_MruNode ftcfamily, FT_Pointer ftcquery ) { FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; FTC_BasicQuery query = (FTC_BasicQuery)ftcquery; return FTC_BASIC_ATTR_COMPARE( &family->attrs, &query->attrs ); } FT_CALLBACK_DEF( FT_Error ) ftc_basic_family_init( FTC_MruNode ftcfamily, FT_Pointer ftcquery, FT_Pointer ftccache ) { FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; FTC_BasicQuery query = (FTC_BasicQuery)ftcquery; FTC_Cache cache = (FTC_Cache)ftccache; FTC_Family_Init( FTC_FAMILY( family ), cache ); family->attrs = query->attrs; return 0; } FT_CALLBACK_DEF( FT_UInt ) ftc_basic_family_get_count( FTC_Family ftcfamily, FTC_Manager manager ) { FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; FT_Error error; FT_Face face; FT_UInt result = 0; error = FTC_Manager_LookupFace( manager, family->attrs.scaler.face_id, &face ); if ( error || !face ) return result; if ( (FT_ULong)face->num_glyphs > FT_UINT_MAX || 0 > face->num_glyphs ) { FT_TRACE1(( "ftc_basic_family_get_count: too large number of glyphs " )); FT_TRACE1(( "in this face, truncated\n", face->num_glyphs )); } if ( !error ) result = (FT_UInt)face->num_glyphs; return result; } FT_CALLBACK_DEF( FT_Error ) ftc_basic_family_load_bitmap( FTC_Family ftcfamily, FT_UInt gindex, FTC_Manager manager, FT_Face *aface ) { FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; FT_Error error; FT_Size size; error = FTC_Manager_LookupSize( manager, &family->attrs.scaler, &size ); if ( !error ) { FT_Face face = size->face; error = FT_Load_Glyph( face, gindex, family->attrs.load_flags | FT_LOAD_RENDER ); if ( !error ) *aface = face; } return error; } FT_CALLBACK_DEF( FT_Error ) ftc_basic_family_load_glyph( FTC_Family ftcfamily, FT_UInt gindex, FTC_Cache cache, FT_Glyph *aglyph ) { FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily; FT_Error error; FTC_Scaler scaler = &family->attrs.scaler; FT_Face face; FT_Size size; /* we will now load the glyph image */ error = FTC_Manager_LookupSize( cache->manager, scaler, &size ); if ( !error ) { face = size->face; error = FT_Load_Glyph( face, gindex, family->attrs.load_flags ); if ( !error ) { if ( face->glyph->format == FT_GLYPH_FORMAT_BITMAP || face->glyph->format == FT_GLYPH_FORMAT_OUTLINE ) { /* ok, copy it */ FT_Glyph glyph; error = FT_Get_Glyph( face->glyph, &glyph ); if ( !error ) { *aglyph = glyph; goto Exit; } } else error = FTC_Err_Invalid_Argument; } } Exit: return error; } FT_CALLBACK_DEF( FT_Bool ) ftc_basic_gnode_compare_faceid( FTC_Node ftcgnode, FT_Pointer ftcface_id, FTC_Cache cache ) { FTC_GNode gnode = (FTC_GNode)ftcgnode; FTC_FaceID face_id = (FTC_FaceID)ftcface_id; FTC_BasicFamily family = (FTC_BasicFamily)gnode->family; FT_Bool result; result = FT_BOOL( family->attrs.scaler.face_id == face_id ); if ( result ) { /* we must call this function to avoid this node from appearing * in later lookups with the same face_id! */ FTC_GNode_UnselectFamily( gnode, cache ); } return result; } /* * * basic image cache * */ FT_CALLBACK_TABLE_DEF const FTC_IFamilyClassRec ftc_basic_image_family_class = { { sizeof ( FTC_BasicFamilyRec ), ftc_basic_family_compare, ftc_basic_family_init, 0, /* FTC_MruNode_ResetFunc */ 0 /* FTC_MruNode_DoneFunc */ }, ftc_basic_family_load_glyph }; FT_CALLBACK_TABLE_DEF const FTC_GCacheClassRec ftc_basic_image_cache_class = { { ftc_inode_new, ftc_inode_weight, ftc_gnode_compare, ftc_basic_gnode_compare_faceid, ftc_inode_free, sizeof ( FTC_GCacheRec ), ftc_gcache_init, ftc_gcache_done }, (FTC_MruListClass)&ftc_basic_image_family_class }; /* documentation is in ftcache.h */ FT_EXPORT_DEF( FT_Error ) FTC_ImageCache_New( FTC_Manager manager, FTC_ImageCache *acache ) { return FTC_GCache_New( manager, &ftc_basic_image_cache_class, (FTC_GCache*)acache ); } /* documentation is in ftcache.h */ FT_EXPORT_DEF( FT_Error ) FTC_ImageCache_Lookup( FTC_ImageCache cache, FTC_ImageType type, FT_UInt gindex, FT_Glyph *aglyph, FTC_Node *anode ) { FTC_BasicQueryRec query; FTC_Node node = 0; /* make compiler happy */ FT_Error error; FT_PtrDist hash; /* some argument checks are delayed to FTC_Cache_Lookup */ if ( !aglyph ) { error = FTC_Err_Invalid_Argument; goto Exit; } *aglyph = NULL; if ( anode ) *anode = NULL; #if defined( FT_CONFIG_OPTION_OLD_INTERNALS ) && ( FT_INT_MAX > 0xFFFFU ) /* * This one is a major hack used to detect whether we are passed a * regular FTC_ImageType handle, or a legacy FTC_OldImageDesc one. */ if ( (FT_ULong)type->width >= 0x10000L ) { FTC_OldImageDesc desc = (FTC_OldImageDesc)type; query.attrs.scaler.face_id = desc->font.face_id; query.attrs.scaler.width = desc->font.pix_width; query.attrs.scaler.height = desc->font.pix_height; query.attrs.load_flags = desc->flags; } else #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */ { if ( (FT_ULong)(type->flags - FT_INT_MIN) > FT_UINT_MAX ) { FT_TRACE1(( "FTC_ImageCache_Lookup: higher bits in load_flags" )); FT_TRACE1(( "0x%x are dropped\n", (type->flags & ~((FT_ULong)FT_UINT_MAX)) )); } query.attrs.scaler.face_id = type->face_id; query.attrs.scaler.width = type->width; query.attrs.scaler.height = type->height; query.attrs.load_flags = (FT_UInt)type->flags; } query.attrs.scaler.pixel = 1; query.attrs.scaler.x_res = 0; /* make compilers happy */ query.attrs.scaler.y_res = 0; hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex; #if 1 /* inlining is about 50% faster! */ FTC_GCACHE_LOOKUP_CMP( cache, ftc_basic_family_compare, FTC_GNode_Compare, hash, gindex, &query, node, error ); #else error = FTC_GCache_Lookup( FTC_GCACHE( cache ), hash, gindex, FTC_GQUERY( &query ), &node ); #endif if ( !error ) { *aglyph = FTC_INODE( node )->glyph; if ( anode ) { *anode = node; node->ref_count++; } } Exit: return error; } /* documentation is in ftcache.h */ FT_EXPORT_DEF( FT_Error ) FTC_ImageCache_LookupScaler( FTC_ImageCache cache, FTC_Scaler scaler, FT_ULong load_flags, FT_UInt gindex, FT_Glyph *aglyph, FTC_Node *anode ) { FTC_BasicQueryRec query; FTC_Node node = 0; /* make compiler happy */ FT_Error error; FT_PtrDist hash; /* some argument checks are delayed to FTC_Cache_Lookup */ if ( !aglyph || !scaler ) { error = FTC_Err_Invalid_Argument; goto Exit; } *aglyph = NULL; if ( anode ) *anode = NULL; /* FT_Load_Glyph(), FT_Load_Char() take FT_UInt flags */ if ( load_flags > FT_UINT_MAX ) { FT_TRACE1(( "FTC_ImageCache_LookupScaler: higher bits in load_flags" )); FT_TRACE1(( "0x%x are dropped\n", (load_flags & ~((FT_ULong)FT_UINT_MAX)) )); } query.attrs.scaler = scaler[0]; query.attrs.load_flags = (FT_UInt)load_flags; hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex; FTC_GCACHE_LOOKUP_CMP( cache, ftc_basic_family_compare, FTC_GNode_Compare, hash, gindex, &query, node, error ); if ( !error ) { *aglyph = FTC_INODE( node )->glyph; if ( anode ) { *anode = node; node->ref_count++; } } Exit: return error; } #ifdef FT_CONFIG_OPTION_OLD_INTERNALS /* yet another backwards-legacy structure */ typedef struct FTC_OldImage_Desc_ { FTC_FontRec font; FT_UInt image_type; } FTC_OldImage_Desc; #define FTC_OLD_IMAGE_FORMAT( x ) ( (x) & 7 ) #define ftc_old_image_format_bitmap 0x0000 #define ftc_old_image_format_outline 0x0001 #define ftc_old_image_format_mask 0x000F #define ftc_old_image_flag_monochrome 0x0010 #define ftc_old_image_flag_unhinted 0x0020 #define ftc_old_image_flag_autohinted 0x0040 #define ftc_old_image_flag_unscaled 0x0080 #define ftc_old_image_flag_no_sbits 0x0100 /* monochrome bitmap */ #define ftc_old_image_mono ftc_old_image_format_bitmap | \ ftc_old_image_flag_monochrome /* anti-aliased bitmap */ #define ftc_old_image_grays ftc_old_image_format_bitmap /* scaled outline */ #define ftc_old_image_outline ftc_old_image_format_outline static void ftc_image_type_from_old_desc( FTC_ImageType typ, FTC_OldImage_Desc* desc ) { typ->face_id = desc->font.face_id; typ->width = desc->font.pix_width; typ->height = desc->font.pix_height; /* convert image type flags to load flags */ { FT_UInt load_flags = FT_LOAD_DEFAULT; FT_UInt type = desc->image_type; /* determine load flags, depending on the font description's */ /* image type */ if ( FTC_OLD_IMAGE_FORMAT( type ) == ftc_old_image_format_bitmap ) { if ( type & ftc_old_image_flag_monochrome ) load_flags |= FT_LOAD_MONOCHROME; /* disable embedded bitmaps loading if necessary */ if ( type & ftc_old_image_flag_no_sbits ) load_flags |= FT_LOAD_NO_BITMAP; } else { /* we want an outline, don't load embedded bitmaps */ load_flags |= FT_LOAD_NO_BITMAP; if ( type & ftc_old_image_flag_unscaled ) load_flags |= FT_LOAD_NO_SCALE; } /* always render glyphs to bitmaps */ load_flags |= FT_LOAD_RENDER; if ( type & ftc_old_image_flag_unhinted ) load_flags |= FT_LOAD_NO_HINTING; if ( type & ftc_old_image_flag_autohinted ) load_flags |= FT_LOAD_FORCE_AUTOHINT; typ->flags = load_flags; } } FT_EXPORT( FT_Error ) FTC_Image_Cache_New( FTC_Manager manager, FTC_ImageCache *acache ); FT_EXPORT( FT_Error ) FTC_Image_Cache_Lookup( FTC_ImageCache icache, FTC_OldImage_Desc* desc, FT_UInt gindex, FT_Glyph *aglyph ); FT_EXPORT_DEF( FT_Error ) FTC_Image_Cache_New( FTC_Manager manager, FTC_ImageCache *acache ) { return FTC_ImageCache_New( manager, (FTC_ImageCache*)acache ); } FT_EXPORT_DEF( FT_Error ) FTC_Image_Cache_Lookup( FTC_ImageCache icache, FTC_OldImage_Desc* desc, FT_UInt gindex, FT_Glyph *aglyph ) { FTC_ImageTypeRec type0; if ( !desc ) return FTC_Err_Invalid_Argument; ftc_image_type_from_old_desc( &type0, desc ); return FTC_ImageCache_Lookup( (FTC_ImageCache)icache, &type0, gindex, aglyph, NULL ); } #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */ /* * * basic small bitmap cache * */ FT_CALLBACK_TABLE_DEF const FTC_SFamilyClassRec ftc_basic_sbit_family_class = { { sizeof( FTC_BasicFamilyRec ), ftc_basic_family_compare, ftc_basic_family_init, 0, /* FTC_MruNode_ResetFunc */ 0 /* FTC_MruNode_DoneFunc */ }, ftc_basic_family_get_count, ftc_basic_family_load_bitmap }; FT_CALLBACK_TABLE_DEF const FTC_GCacheClassRec ftc_basic_sbit_cache_class = { { ftc_snode_new, ftc_snode_weight, ftc_snode_compare, ftc_basic_gnode_compare_faceid, ftc_snode_free, sizeof ( FTC_GCacheRec ), ftc_gcache_init, ftc_gcache_done }, (FTC_MruListClass)&ftc_basic_sbit_family_class }; /* documentation is in ftcache.h */ FT_EXPORT_DEF( FT_Error ) FTC_SBitCache_New( FTC_Manager manager, FTC_SBitCache *acache ) { return FTC_GCache_New( manager, &ftc_basic_sbit_cache_class, (FTC_GCache*)acache ); } /* documentation is in ftcache.h */ FT_EXPORT_DEF( FT_Error ) FTC_SBitCache_Lookup( FTC_SBitCache cache, FTC_ImageType type, FT_UInt gindex, FTC_SBit *ansbit, FTC_Node *anode ) { FT_Error error; FTC_BasicQueryRec query; FTC_Node node = 0; /* make compiler happy */ FT_PtrDist hash; if ( anode ) *anode = NULL; /* other argument checks delayed to FTC_Cache_Lookup */ if ( !ansbit ) return FTC_Err_Invalid_Argument; *ansbit = NULL; #if defined( FT_CONFIG_OPTION_OLD_INTERNALS ) && ( FT_INT_MAX > 0xFFFFU ) /* This one is a major hack used to detect whether we are passed a * regular FTC_ImageType handle, or a legacy FTC_OldImageDesc one. */ if ( (FT_ULong)type->width >= 0x10000L ) { FTC_OldImageDesc desc = (FTC_OldImageDesc)type; query.attrs.scaler.face_id = desc->font.face_id; query.attrs.scaler.width = desc->font.pix_width; query.attrs.scaler.height = desc->font.pix_height; query.attrs.load_flags = desc->flags; } else #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */ { if ( (FT_ULong)(type->flags - FT_INT_MIN) > FT_UINT_MAX ) { FT_TRACE1(( "FTC_ImageCache_Lookup: higher bits in load_flags" )); FT_TRACE1(( "0x%x are dropped\n", (type->flags & ~((FT_ULong)FT_UINT_MAX)) )); } query.attrs.scaler.face_id = type->face_id; query.attrs.scaler.width = type->width; query.attrs.scaler.height = type->height; query.attrs.load_flags = (FT_UInt)type->flags; } query.attrs.scaler.pixel = 1; query.attrs.scaler.x_res = 0; /* make compilers happy */ query.attrs.scaler.y_res = 0; /* beware, the hash must be the same for all glyph ranges! */ hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex / FTC_SBIT_ITEMS_PER_NODE; #if 1 /* inlining is about 50% faster! */ FTC_GCACHE_LOOKUP_CMP( cache, ftc_basic_family_compare, FTC_SNode_Compare, hash, gindex, &query, node, error ); #else error = FTC_GCache_Lookup( FTC_GCACHE( cache ), hash, gindex, FTC_GQUERY( &query ), &node ); #endif if ( error ) goto Exit; *ansbit = FTC_SNODE( node )->sbits + ( gindex - FTC_GNODE( node )->gindex ); if ( anode ) { *anode = node; node->ref_count++; } Exit: return error; } /* documentation is in ftcache.h */ FT_EXPORT_DEF( FT_Error ) FTC_SBitCache_LookupScaler( FTC_SBitCache cache, FTC_Scaler scaler, FT_ULong load_flags, FT_UInt gindex, FTC_SBit *ansbit, FTC_Node *anode ) { FT_Error error; FTC_BasicQueryRec query; FTC_Node node = 0; /* make compiler happy */ FT_PtrDist hash; if ( anode ) *anode = NULL; /* other argument checks delayed to FTC_Cache_Lookup */ if ( !ansbit || !scaler ) return FTC_Err_Invalid_Argument; *ansbit = NULL; /* FT_Load_Glyph(), FT_Load_Char() take FT_UInt flags */ if ( load_flags > FT_UINT_MAX ) { FT_TRACE1(( "FTC_ImageCache_LookupScaler: higher bits in load_flags" )); FT_TRACE1(( "0x%x are dropped\n", (load_flags & ~((FT_ULong)FT_UINT_MAX)) )); } query.attrs.scaler = scaler[0]; query.attrs.load_flags = (FT_UInt)load_flags; /* beware, the hash must be the same for all glyph ranges! */ hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex / FTC_SBIT_ITEMS_PER_NODE; FTC_GCACHE_LOOKUP_CMP( cache, ftc_basic_family_compare, FTC_SNode_Compare, hash, gindex, &query, node, error ); if ( error ) goto Exit; *ansbit = FTC_SNODE( node )->sbits + ( gindex - FTC_GNODE( node )->gindex ); if ( anode ) { *anode = node; node->ref_count++; } Exit: return error; } #ifdef FT_CONFIG_OPTION_OLD_INTERNALS FT_EXPORT( FT_Error ) FTC_SBit_Cache_New( FTC_Manager manager, FTC_SBitCache *acache ); FT_EXPORT( FT_Error ) FTC_SBit_Cache_Lookup( FTC_SBitCache cache, FTC_OldImage_Desc* desc, FT_UInt gindex, FTC_SBit *ansbit ); FT_EXPORT_DEF( FT_Error ) FTC_SBit_Cache_New( FTC_Manager manager, FTC_SBitCache *acache ) { return FTC_SBitCache_New( manager, (FTC_SBitCache*)acache ); } FT_EXPORT_DEF( FT_Error ) FTC_SBit_Cache_Lookup( FTC_SBitCache cache, FTC_OldImage_Desc* desc, FT_UInt gindex, FTC_SBit *ansbit ) { FTC_ImageTypeRec type0; if ( !desc ) return FTC_Err_Invalid_Argument; ftc_image_type_from_old_desc( &type0, desc ); return FTC_SBitCache_Lookup( (FTC_SBitCache)cache, &type0, gindex, ansbit, NULL ); } #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */ /* END */