/***************************************************************************/ /* */ /* ftcsbits.c */ /* */ /* FreeType sbits manager (body). */ /* */ /* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010, 2011 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 "ftcsbits.h" #include FT_INTERNAL_OBJECTS_H #include FT_INTERNAL_DEBUG_H #include FT_ERRORS_H #include "ftccback.h" #include "ftcerror.h" #undef FT_COMPONENT #define FT_COMPONENT trace_cache /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** 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_LOCAL_DEF( void ) ftc_snode_free( FTC_Node ftcsnode, FTC_Cache cache ) { FTC_SNode snode = (FTC_SNode)ftcsnode; FTC_SBit sbit = snode->sbits; FT_UInt count = snode->count; FT_Memory memory = cache->memory; for ( ; count > 0; sbit++, count-- ) FT_FREE( sbit->buffer ); FTC_GNode_Done( FTC_GNODE( snode ), cache ); FT_FREE( snode ); } FT_LOCAL_DEF( void ) FTC_SNode_Free( FTC_SNode snode, FTC_Cache cache ) { ftc_snode_free( FTC_NODE( snode ), cache ); } /* * This function tries to load a small bitmap within a given FTC_SNode. * Note that it returns a non-zero error code _only_ in the case of * out-of-memory condition. For all other errors (e.g., corresponding * to a bad font file), this function will mark the sbit as `unavailable' * and return a value of 0. * * You should also read the comment within the @ftc_snode_compare * function below to see how out-of-memory is handled during a lookup. */ static FT_Error ftc_snode_load( FTC_SNode snode, FTC_Manager manager, FT_UInt gindex, FT_ULong *asize ) { FT_Error error; FTC_GNode gnode = FTC_GNODE( snode ); FTC_Family family = gnode->family; FT_Memory memory = manager->memory; FT_Face face; FTC_SBit sbit; FTC_SFamilyClass clazz; if ( (FT_UInt)(gindex - gnode->gindex) >= snode->count ) { FT_ERROR(( "ftc_snode_load: invalid glyph index" )); return FTC_Err_Invalid_Argument; } sbit = snode->sbits + ( gindex - gnode->gindex ); clazz = (FTC_SFamilyClass)family->clazz; sbit->buffer = 0; error = clazz->family_load_glyph( family, gindex, manager, &face ); if ( error ) goto BadGlyph; { FT_Int temp; FT_GlyphSlot slot = face->glyph; FT_Bitmap* bitmap = &slot->bitmap; FT_Pos xadvance, yadvance; /* FT_GlyphSlot->advance.{x|y} */ if ( slot->format != FT_GLYPH_FORMAT_BITMAP ) { FT_TRACE0(( "ftc_snode_load:" " glyph loaded didn't return a bitmap\n" )); goto BadGlyph; } /* 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 ) /* horizontal advance in pixels */ xadvance = ( slot->advance.x + 32 ) >> 6; yadvance = ( slot->advance.y + 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 ) ) { FT_TRACE2(( "ftc_snode_load:" " glyph too large for small bitmap cache\n")); goto BadGlyph; } 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); /* copy the bitmap into a new buffer -- ignore error */ error = ftc_sbit_copy_bitmap( sbit, bitmap, memory ); /* now, compute size */ if ( asize ) *asize = FT_ABS( sbit->pitch ) * sbit->height; } /* 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 && error != FTC_Err_Out_Of_Memory ) { BadGlyph: sbit->width = 255; sbit->height = 0; sbit->buffer = NULL; error = FTC_Err_Ok; if ( asize ) *asize = 0; } return error; } FT_LOCAL_DEF( FT_Error ) FTC_SNode_New( FTC_SNode *psnode, FTC_GQuery gquery, FTC_Cache cache ) { FT_Memory memory = cache->memory; FT_Error error; FTC_SNode snode = NULL; FT_UInt gindex = gquery->gindex; FTC_Family family = gquery->family; FTC_SFamilyClass clazz = FTC_CACHE__SFAMILY_CLASS( cache ); FT_UInt total; FT_UInt node_count; total = clazz->family_get_count( family, cache->manager ); if ( total == 0 || gindex >= total ) { error = FTC_Err_Invalid_Argument; goto Exit; } if ( !FT_NEW( snode ) ) { FT_UInt count, start; start = gindex - ( gindex % FTC_SBIT_ITEMS_PER_NODE ); count = total - start; if ( count > FTC_SBIT_ITEMS_PER_NODE ) count = FTC_SBIT_ITEMS_PER_NODE; FTC_GNode_Init( FTC_GNODE( snode ), start, family ); snode->count = count; for ( node_count = 0; node_count < count; node_count++ ) { snode->sbits[node_count].width = 255; } error = ftc_snode_load( snode, cache->manager, gindex, NULL ); if ( error ) { FTC_SNode_Free( snode, cache ); snode = NULL; } } Exit: *psnode = snode; return error; } FT_LOCAL_DEF( FT_Error ) ftc_snode_new( FTC_Node *ftcpsnode, FT_Pointer ftcgquery, FTC_Cache cache ) { FTC_SNode *psnode = (FTC_SNode*)ftcpsnode; FTC_GQuery gquery = (FTC_GQuery)ftcgquery; return FTC_SNode_New( psnode, gquery, cache ); } FT_LOCAL_DEF( FT_Offset ) ftc_snode_weight( FTC_Node ftcsnode, FTC_Cache cache ) { FTC_SNode snode = (FTC_SNode)ftcsnode; FT_UInt count = snode->count; FTC_SBit sbit = snode->sbits; FT_Int pitch; FT_Offset size; FT_UNUSED( cache ); FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE ); /* the node itself */ size = sizeof ( *snode ); 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; } #if 0 FT_LOCAL_DEF( FT_Offset ) FTC_SNode_Weight( FTC_SNode snode ) { return ftc_snode_weight( FTC_NODE( snode ), NULL ); } #endif /* 0 */ FT_LOCAL_DEF( FT_Bool ) ftc_snode_compare( FTC_Node ftcsnode, FT_Pointer ftcgquery, FTC_Cache cache, FT_Bool* list_changed ) { FTC_SNode snode = (FTC_SNode)ftcsnode; FTC_GQuery gquery = (FTC_GQuery)ftcgquery; FTC_GNode gnode = FTC_GNODE( snode ); FT_UInt gindex = gquery->gindex; FT_Bool result; if (list_changed) *list_changed = FALSE; result = FT_BOOL( gnode->family == gquery->family && (FT_UInt)( gindex - gnode->gindex ) < snode->count ); if ( result ) { /* check if we need to load the glyph bitmap now */ FTC_SBit sbit = snode->sbits + ( gindex - gnode->gindex ); /* * The following code illustrates what to do when you want to * perform operations that may fail within a lookup function. * * Here, we want to load a small bitmap on-demand; we thus * need to call the `ftc_snode_load' function which may return * a non-zero error code only when we are out of memory (OOM). * * The correct thing to do is to use @FTC_CACHE_TRYLOOP and * @FTC_CACHE_TRYLOOP_END in order to implement a retry loop * that is capable of flushing the cache incrementally when * an OOM errors occur. * * However, we need to `lock' the node before this operation to * prevent it from being flushed within the loop. * * When we exit the loop, we unlock the node, then check the `error' * variable. If it is non-zero, this means that the cache was * completely flushed and that no usable memory was found to load * the bitmap. * * We then prefer to return a value of 0 (i.e., NO MATCH). This * ensures that the caller will try to allocate a new node. * This operation consequently _fail_ and the lookup function * returns the appropriate OOM error code. * * Note that `buffer == NULL && width == 255' is a hack used to * tag `unavailable' bitmaps in the array. We should never try * to load these. * */ if ( sbit->buffer == NULL && sbit->width == 255 ) { FT_ULong size; FT_Error error; ftcsnode->ref_count++; /* lock node to prevent flushing */ /* in retry loop */ FTC_CACHE_TRYLOOP( cache ) { error = ftc_snode_load( snode, cache->manager, gindex, &size ); } FTC_CACHE_TRYLOOP_END( list_changed ); ftcsnode->ref_count--; /* unlock the node */ if ( error ) result = 0; else cache->manager->cur_weight += size; } } return result; } #ifdef FTC_INLINE FT_LOCAL_DEF( FT_Bool ) FTC_SNode_Compare( FTC_SNode snode, FTC_GQuery gquery, FTC_Cache cache, FT_Bool* list_changed ) { return ftc_snode_compare( FTC_NODE( snode ), gquery, cache, list_changed ); } #endif /* END */