| /***************************************************************************/ |
| /* */ |
| /* ftcsbits.c */ |
| /* */ |
| /* FreeType sbits manager (body). */ |
| /* */ |
| /* Copyright 2000-2001, 2002 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_SMALL_BITMAPS_H |
| #include FT_CACHE_INTERNAL_GLYPH_H |
| #include FT_INTERNAL_OBJECTS_H |
| #include FT_INTERNAL_DEBUG_H |
| #include FT_ERRORS_H |
| |
| #include "ftcerror.h" |
| |
| |
| #define FTC_SBIT_ITEMS_PER_NODE 16 |
| |
| |
| typedef struct FTC_SBitNodeRec_* FTC_SBitNode; |
| |
| typedef struct FTC_SBitNodeRec_ |
| { |
| FTC_GlyphNodeRec gnode; |
| FTC_SBitRec sbits[FTC_SBIT_ITEMS_PER_NODE]; |
| |
| } FTC_SBitNodeRec; |
| |
| |
| #define FTC_SBIT_NODE( x ) ( (FTC_SBitNode)( x ) ) |
| |
| |
| typedef struct FTC_SBitQueryRec_ |
| { |
| FTC_GlyphQueryRec gquery; |
| FTC_ImageTypeRec type; |
| |
| } FTC_SBitQueryRec, *FTC_SBitQuery; |
| |
| |
| #define FTC_SBIT_QUERY( x ) ( (FTC_SBitQuery)( x ) ) |
| |
| |
| typedef struct FTC_SBitFamilyRec_* FTC_SBitFamily; |
| |
| /* sbit family structure */ |
| typedef struct FTC_SBitFamilyRec_ |
| { |
| FTC_GlyphFamilyRec gfam; |
| FTC_ImageTypeRec type; |
| |
| } FTC_SBitFamilyRec; |
| |
| |
| #define FTC_SBIT_FAMILY( x ) ( (FTC_SBitFamily)( x ) ) |
| #define FTC_SBIT_FAMILY_MEMORY( x ) FTC_GLYPH_FAMILY_MEMORY( &( x )->cset ) |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** SBIT CACHE NODES *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| static FT_Error |
| ftc_sbit_copy_bitmap( FTC_SBit sbit, |
| FT_Bitmap* bitmap, |
| FT_Memory memory ) |
| { |
| FT_Error error; |
| FT_Int pitch = bitmap->pitch; |
| FT_ULong size; |
| |
| |
| if ( pitch < 0 ) |
| pitch = -pitch; |
| |
| size = (FT_ULong)( pitch * bitmap->rows ); |
| |
| if ( !FT_ALLOC( sbit->buffer, size ) ) |
| FT_MEM_COPY( sbit->buffer, bitmap->buffer, size ); |
| |
| return error; |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| ftc_sbit_node_done( FTC_SBitNode snode, |
| FTC_Cache cache ) |
| { |
| FTC_SBit sbit = snode->sbits; |
| FT_UInt count = FTC_GLYPH_NODE( snode )->item_count; |
| FT_Memory memory = cache->memory; |
| |
| |
| for ( ; count > 0; sbit++, count-- ) |
| FT_FREE( sbit->buffer ); |
| |
| ftc_glyph_node_done( FTC_GLYPH_NODE( snode ), cache ); |
| } |
| |
| |
| static FT_Error |
| ftc_sbit_node_load( FTC_SBitNode snode, |
| FTC_Manager manager, |
| FTC_SBitFamily sfam, |
| FT_UInt gindex, |
| FT_ULong *asize ) |
| { |
| FT_Error error; |
| FTC_GlyphNode gnode = FTC_GLYPH_NODE( snode ); |
| FT_Memory memory; |
| FT_Face face; |
| FT_Size size; |
| FTC_SBit sbit; |
| |
| |
| if ( gindex < (FT_UInt)gnode->item_start || |
| gindex >= (FT_UInt)gnode->item_start + gnode->item_count ) |
| { |
| FT_ERROR(( "ftc_sbit_node_load: invalid glyph index" )); |
| return FTC_Err_Invalid_Argument; |
| } |
| |
| memory = manager->library->memory; |
| |
| sbit = snode->sbits + ( gindex - gnode->item_start ); |
| |
| error = FTC_Manager_Lookup_Size( manager, &sfam->type.font, |
| &face, &size ); |
| if ( !error ) |
| { |
| /* by default, indicates a `missing' glyph */ |
| sbit->buffer = 0; |
| |
| error = FT_Load_Glyph( face, gindex, sfam->type.flags | FT_LOAD_RENDER ); |
| if ( !error ) |
| { |
| FT_Int temp; |
| FT_GlyphSlot slot = face->glyph; |
| FT_Bitmap* bitmap = &slot->bitmap; |
| FT_Int xadvance, yadvance; |
| |
| |
| /* check that our values fit into 8-bit containers! */ |
| /* If this is not the case, our bitmap is too large */ |
| /* and we will leave it as `missing' with sbit.buffer = 0 */ |
| |
| #define CHECK_CHAR( d ) ( temp = (FT_Char)d, temp == d ) |
| #define CHECK_BYTE( d ) ( temp = (FT_Byte)d, temp == d ) |
| |
| /* XXX: FIXME: add support for vertical layouts maybe */ |
| |
| /* horizontal advance in pixels */ |
| xadvance = ( slot->metrics.horiAdvance + 32 ) >> 6; |
| yadvance = ( slot->metrics.vertAdvance + 32 ) >> 6; |
| |
| if ( CHECK_BYTE( bitmap->rows ) && |
| CHECK_BYTE( bitmap->width ) && |
| CHECK_CHAR( bitmap->pitch ) && |
| CHECK_CHAR( slot->bitmap_left ) && |
| CHECK_CHAR( slot->bitmap_top ) && |
| CHECK_CHAR( xadvance ) && |
| CHECK_CHAR( yadvance ) ) |
| { |
| sbit->width = (FT_Byte)bitmap->width; |
| sbit->height = (FT_Byte)bitmap->rows; |
| sbit->pitch = (FT_Char)bitmap->pitch; |
| sbit->left = (FT_Char)slot->bitmap_left; |
| sbit->top = (FT_Char)slot->bitmap_top; |
| sbit->xadvance = (FT_Char)xadvance; |
| sbit->yadvance = (FT_Char)yadvance; |
| sbit->format = (FT_Byte)bitmap->pixel_mode; |
| sbit->max_grays = (FT_Byte)(bitmap->num_grays - 1); |
| |
| /* grab the bitmap when possible - this is a hack! */ |
| if ( slot->flags & FT_GLYPH_OWN_BITMAP ) |
| { |
| slot->flags &= ~FT_GLYPH_OWN_BITMAP; |
| sbit->buffer = bitmap->buffer; |
| } |
| else |
| { |
| /* copy the bitmap into a new buffer -- ignore error */ |
| error = ftc_sbit_copy_bitmap( sbit, bitmap, memory ); |
| } |
| |
| /* now, compute size */ |
| if ( asize ) |
| *asize = ABS( sbit->pitch ) * sbit->height; |
| |
| } /* glyph dimensions ok */ |
| |
| } /* glyph loading successful */ |
| |
| /* ignore the errors that might have occurred -- */ |
| /* we mark unloaded glyphs with `sbit.buffer == 0' */ |
| /* and 'width == 255', 'height == 0' */ |
| /* */ |
| if ( error ) |
| { |
| sbit->width = 255; |
| error = 0; |
| /* sbit->buffer == NULL too! */ |
| } |
| } |
| |
| return error; |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_Error ) |
| ftc_sbit_node_init( FTC_SBitNode snode, |
| FTC_GlyphQuery gquery, |
| FTC_Cache cache ) |
| { |
| FT_Error error; |
| |
| |
| ftc_glyph_node_init( FTC_GLYPH_NODE( snode ), |
| gquery->gindex, |
| FTC_GLYPH_FAMILY( gquery->query.family ) ); |
| |
| error = ftc_sbit_node_load( snode, |
| cache->manager, |
| FTC_SBIT_FAMILY( FTC_QUERY( gquery )->family ), |
| gquery->gindex, |
| NULL ); |
| if ( error ) |
| ftc_glyph_node_done( FTC_GLYPH_NODE( snode ), cache ); |
| |
| return error; |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_ULong ) |
| ftc_sbit_node_weight( FTC_SBitNode snode ) |
| { |
| FTC_GlyphNode gnode = FTC_GLYPH_NODE( snode ); |
| FT_UInt count = gnode->item_count; |
| FTC_SBit sbit = snode->sbits; |
| FT_Int pitch; |
| FT_ULong size; |
| |
| |
| /* the node itself */ |
| size = sizeof ( *snode ); |
| |
| /* the sbit records */ |
| size += FTC_GLYPH_NODE( snode )->item_count * sizeof ( FTC_SBitRec ); |
| |
| for ( ; count > 0; count--, sbit++ ) |
| { |
| if ( sbit->buffer ) |
| { |
| pitch = sbit->pitch; |
| if ( pitch < 0 ) |
| pitch = -pitch; |
| |
| /* add the size of a given glyph image */ |
| size += pitch * sbit->height; |
| } |
| } |
| |
| return size; |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_Bool ) |
| ftc_sbit_node_compare( FTC_SBitNode snode, |
| FTC_SBitQuery squery, |
| FTC_Cache cache ) |
| { |
| FTC_GlyphQuery gquery = FTC_GLYPH_QUERY( squery ); |
| FTC_GlyphNode gnode = FTC_GLYPH_NODE( snode ); |
| FT_Bool result; |
| |
| |
| result = ftc_glyph_node_compare( gnode, gquery ); |
| if ( result ) |
| { |
| /* check if we need to load the glyph bitmap now */ |
| FT_UInt gindex = gquery->gindex; |
| FTC_SBit sbit = snode->sbits + ( gindex - gnode->item_start ); |
| |
| |
| if ( sbit->buffer == NULL && sbit->width != 255 ) |
| { |
| FT_ULong size; |
| |
| |
| /* yes, it's safe to ignore errors here */ |
| ftc_sbit_node_load( snode, |
| cache->manager, |
| FTC_SBIT_FAMILY( FTC_QUERY( squery )->family ), |
| gindex, |
| &size ); |
| |
| cache->manager->cur_weight += size; |
| } |
| } |
| |
| return result; |
| } |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** SBITS FAMILIES *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| FT_CALLBACK_DEF( FT_Error ) |
| ftc_sbit_family_init( FTC_SBitFamily sfam, |
| FTC_SBitQuery squery, |
| FTC_Cache cache ) |
| { |
| FTC_Manager manager = cache->manager; |
| FT_Error error; |
| FT_Face face; |
| |
| |
| sfam->type = squery->type; |
| |
| /* we need to compute "cquery.item_total" now */ |
| error = FTC_Manager_Lookup_Face( manager, |
| squery->type.font.face_id, |
| &face ); |
| if ( !error ) |
| { |
| error = ftc_glyph_family_init( FTC_GLYPH_FAMILY( sfam ), |
| FTC_IMAGE_TYPE_HASH( &sfam->type ), |
| FTC_SBIT_ITEMS_PER_NODE, |
| face->num_glyphs, |
| FTC_GLYPH_QUERY( squery ), |
| cache ); |
| } |
| |
| return error; |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_Bool ) |
| ftc_sbit_family_compare( FTC_SBitFamily sfam, |
| FTC_SBitQuery squery ) |
| { |
| FT_Bool result; |
| |
| |
| /* we need to set the "cquery.cset" field or our query for */ |
| /* faster glyph comparisons in ftc_sbit_node_compare */ |
| /* */ |
| result = FT_BOOL( FTC_IMAGE_TYPE_COMPARE( &sfam->type, &squery->type ) ); |
| if ( result ) |
| FTC_GLYPH_FAMILY_FOUND( sfam, squery ); |
| |
| return result; |
| } |
| |
| |
| /*************************************************************************/ |
| /*************************************************************************/ |
| /***** *****/ |
| /***** SBITS CACHE *****/ |
| /***** *****/ |
| /*************************************************************************/ |
| /*************************************************************************/ |
| |
| |
| FT_CALLBACK_TABLE_DEF |
| const FTC_Cache_ClassRec ftc_sbit_cache_class = |
| { |
| sizeof ( FTC_CacheRec ), |
| (FTC_Cache_InitFunc) ftc_cache_init, |
| (FTC_Cache_ClearFunc)ftc_cache_clear, |
| (FTC_Cache_DoneFunc) ftc_cache_done, |
| |
| sizeof ( FTC_SBitFamilyRec ), |
| (FTC_Family_InitFunc) ftc_sbit_family_init, |
| (FTC_Family_CompareFunc)ftc_sbit_family_compare, |
| (FTC_Family_DoneFunc) ftc_glyph_family_done, |
| |
| sizeof ( FTC_SBitNodeRec ), |
| (FTC_Node_InitFunc) ftc_sbit_node_init, |
| (FTC_Node_WeightFunc) ftc_sbit_node_weight, |
| (FTC_Node_CompareFunc)ftc_sbit_node_compare, |
| (FTC_Node_DoneFunc) ftc_sbit_node_done |
| }; |
| |
| |
| /* documentation is in ftcsbits.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FTC_SBitCache_New( FTC_Manager manager, |
| FTC_SBitCache *acache ) |
| { |
| return FTC_Manager_Register_Cache( manager, |
| &ftc_sbit_cache_class, |
| (FTC_Cache*)acache ); |
| } |
| |
| |
| /* documentation is in ftcsbits.h */ |
| |
| #ifdef FTC_CACHE_USE_INLINE |
| |
| #define GEN_CACHE_FAMILY_COMPARE( f, q, c ) \ |
| ftc_sbit_family_compare( (FTC_SBitFamily)(f), (FTC_SBitQuery)(q) ) |
| |
| #define GEN_CACHE_NODE_COMPARE( n, q, c ) \ |
| ftc_sbit_node_compare( (FTC_SBitNode)(n), (FTC_SBitQuery)(q), c ) |
| |
| #define GEN_CACHE_LOOKUP ftc_sbit_cache_lookup |
| #include "ftccache.i" |
| |
| #else /* !FTC_CACHE_USE_INLINE */ |
| |
| #define ftc_sbit_cache_lookup ftc_cache_lookup |
| |
| #endif /* !FTC_CACHE_USE_INLINE */ |
| |
| 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_SBitQueryRec squery; |
| FTC_SBitNode node; |
| |
| |
| /* other argument checks delayed to ftc_cache_lookup */ |
| if ( !ansbit ) |
| return FTC_Err_Invalid_Argument; |
| |
| *ansbit = NULL; |
| |
| if ( anode ) |
| *anode = NULL; |
| |
| squery.gquery.gindex = gindex; |
| squery.type = *type; |
| |
| error = ftc_sbit_cache_lookup( FTC_CACHE( cache ), |
| FTC_QUERY( &squery ), |
| (FTC_Node*)&node ); |
| if ( !error ) |
| { |
| *ansbit = node->sbits + ( gindex - FTC_GLYPH_NODE( node )->item_start ); |
| |
| if ( anode ) |
| { |
| *anode = FTC_NODE( node ); |
| FTC_NODE( node )->ref_count++; |
| } |
| } |
| return error; |
| } |
| |
| |
| /* backwards-compatibility functions */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FTC_SBit_Cache_New( FTC_Manager manager, |
| FTC_SBit_Cache *acache ) |
| { |
| return FTC_SBitCache_New( manager, (FTC_SBitCache*)acache ); |
| } |
| |
| |
| FT_EXPORT_DEF( FT_Error ) |
| FTC_SBit_Cache_Lookup( FTC_SBit_Cache cache, |
| FTC_Image_Desc* desc, |
| FT_UInt gindex, |
| FTC_SBit *ansbit ) |
| { |
| FTC_ImageTypeRec type0; |
| |
| |
| if ( !desc ) |
| return FTC_Err_Invalid_Argument; |
| |
| type0.font = desc->font; |
| type0.flags = 0; |
| |
| /* 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_image_format( type ) == ftc_image_format_bitmap ) |
| { |
| if ( type & ftc_image_flag_monochrome ) |
| load_flags |= FT_LOAD_MONOCHROME; |
| |
| /* disable embedded bitmaps loading if necessary */ |
| if ( type & ftc_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_image_flag_unscaled ) |
| load_flags |= FT_LOAD_NO_SCALE; |
| } |
| |
| /* always render glyphs to bitmaps */ |
| load_flags |= FT_LOAD_RENDER; |
| |
| if ( type & ftc_image_flag_unhinted ) |
| load_flags |= FT_LOAD_NO_HINTING; |
| |
| if ( type & ftc_image_flag_autohinted ) |
| load_flags |= FT_LOAD_FORCE_AUTOHINT; |
| |
| type0.flags = load_flags; |
| } |
| |
| return FTC_SBitCache_Lookup( (FTC_SBitCache)cache, |
| &type0, |
| gindex, |
| ansbit, |
| NULL ); |
| } |
| |
| |
| /* END */ |