Index: src/cache/ftcsbits.c |
diff --git a/src/cache/ftcsbits.c b/src/cache/ftcsbits.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6df1c19930c4b7595024e344d32c73d342b97465 |
--- /dev/null |
+++ b/src/cache/ftcsbits.c |
@@ -0,0 +1,421 @@ |
+/***************************************************************************/ |
+/* */ |
+/* ftcsbits.c */ |
+/* */ |
+/* FreeType sbits manager (body). */ |
+/* */ |
+/* Copyright 2000-2006, 2009-2011, 2013 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 "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 FT_THROW( 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 && FT_ERR_NEQ( error, Out_Of_Memory ) ) |
+ { |
+ BadGlyph: |
+ sbit->width = 255; |
+ sbit->height = 0; |
+ sbit->buffer = NULL; |
+ error = FT_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 = FT_THROW( 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 */ |