From e5680279b21106173e342eab28552ae0e027196a Mon Sep 17 00:00:00 2001 From: David Turner Date: Sat, 26 Feb 2005 00:12:04 +0000 Subject: [PATCH] * many, many files: several memory optimizations were implemented to drastically reduce the heap usage of FreeType, especially in the case of memory-mapped files. The idea is to avoid loading and decoding tables in the heap, and instead access the raw data whenever possible (i.e. when it doesn't compromise performance). This had several impacts: first, opening vera.ttf uses a ridiculous amount of memory (when the FT_Library footprint is accounted for), until you start loading glyphs. Even then, you'll save at least 20 Kb compared to the non optimized case. performance of various operations, including open/close has also been dramatically improved. More optimisations to come. The auto-hinter eats memory like crazy? This must be stopped... --- ChangeLog | 17 + include/freetype/internal/sfnt.h | 21 ++ include/freetype/internal/tttypes.h | 29 +- src/base/ftdbgmem.c | 10 +- src/cff/cffdrivr.c | 57 +--- src/sfnt/Jamfile | 2 +- src/sfnt/rules.mk | 1 + src/sfnt/sfdriver.c | 3 + src/sfnt/sfnt.c | 1 + src/sfnt/sfobjs.c | 25 +- src/sfnt/ttkern.c | 479 ++++++++++++++++++++++++++++ src/sfnt/ttkern.h | 55 ++++ src/sfnt/ttload.c | 344 +++++++++----------- src/sfnt/ttload.h | 5 - src/sfnt/ttsbit.c | 2 +- src/truetype/ttdriver.c | 53 +-- src/truetype/ttgload.c | 127 ++++++-- src/truetype/ttgload.h | 6 - 18 files changed, 903 insertions(+), 334 deletions(-) create mode 100644 src/sfnt/ttkern.c create mode 100644 src/sfnt/ttkern.h diff --git a/ChangeLog b/ChangeLog index cde05d7a6..5fa7f2ea6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2005-02-25 David Turner + + * many, many files: several memory optimizations were implemented to + drastically reduce the heap usage of FreeType, especially in the case + of memory-mapped files. The idea is to avoid loading and decoding tables + in the heap, and instead access the raw data whenever possible (i.e. + when it doesn't compromise performance). + + This had several impacts: first, opening vera.ttf uses a ridiculous amount + of memory (when the FT_Library footprint is accounted for), until you start + loading glyphs. Even then, you'll save at least 20 Kb compared to the non + optimized case. performance of various operations, including open/close + has also been dramatically improved. + + More optimisations to come. The auto-hinter eats memory like crazy? This + must be stopped... + 2005-02-22 David Turner * src/base/ftdbgmem.c: adding the ability to list all allocation sites diff --git a/include/freetype/internal/sfnt.h b/include/freetype/internal/sfnt.h index 9a23b1f82..bcab14516 100644 --- a/include/freetype/internal/sfnt.h +++ b/include/freetype/internal/sfnt.h @@ -477,6 +477,24 @@ FT_BEGIN_HEADER typedef void (*TT_Free_Table_Func)( TT_Face face ); + /** + * @functype: TT_Face_GetKerningFunc + * + * @description: + * return the horizontal kerning value between two glyphs + * + * @input: + * face :: handle to source face object + * left_glyph :: left glyph index + * right_glyph :: right glyph index + * + * @return: + * kerning value in font units. + */ + typedef FT_Int + (*TT_Face_GetKerningFunc)( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ); /*************************************************************************/ /* */ @@ -534,6 +552,9 @@ FT_BEGIN_HEADER TT_Load_SBit_Image_Func load_sbit_image; TT_Free_Table_Func free_sbits; + /* sett `ttkern.h' */ + TT_Face_GetKerningFunc get_kerning; + /* see `ttpost.h' */ TT_Get_PS_Name_Func get_psname; TT_Free_Table_Func free_psnames; diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h index 183beb8b9..a8adf705d 100644 --- a/include/freetype/internal/tttypes.h +++ b/include/freetype/internal/tttypes.h @@ -310,7 +310,7 @@ FT_BEGIN_HEADER } TT_GaspRec; - +#ifndef FT_OPTIMIZE_MEMORY /*************************************************************************/ /* */ /* */ @@ -360,8 +360,6 @@ FT_BEGIN_HEADER } TT_HdmxRec, *TT_Hdmx; - - /*************************************************************************/ /* */ /* */ @@ -387,6 +385,7 @@ FT_BEGIN_HEADER FT_FWord value; /* kerning value */ } TT_Kern0_PairRec, *TT_Kern0_Pair; +#endif /* !OPTIMIZE_MEMORY */ /*************************************************************************/ @@ -1199,12 +1198,20 @@ FT_BEGIN_HEADER TT_Header header; /* TrueType header table */ TT_HoriHeader horizontal; /* TrueType horizontal header */ +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* horz_metrics; + FT_ULong horz_metrics_size; +#endif TT_MaxProfile max_profile; FT_ULong max_components; FT_Bool vertical_info; TT_VertHeader vertical; /* TT Vertical header, if present */ +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* vert_metrics; + FT_ULong vert_metrics_size; +#endif FT_UShort num_names; /* number of name records */ TT_NameTableRec name_table; /* name table */ @@ -1239,7 +1246,15 @@ FT_BEGIN_HEADER /***********************************************************************/ /* horizontal device metrics */ +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* hdmx_table; + FT_ULong hdmx_table_size; + FT_UInt hdmx_record_count; + FT_ULong hdmx_record_size; + FT_Byte* hdmx_record_sizes; +#else TT_HdmxRec hdmx; +#endif /* grid-fitting and scaling table */ TT_GaspRec gasp; /* the `gasp' table */ @@ -1285,10 +1300,18 @@ FT_BEGIN_HEADER FT_ULong cvt_size; FT_Short* cvt; +#ifdef FT_OPTIMIZE_MEMORY + FT_Byte* kern_table; + FT_ULong kern_table_size; + FT_UInt num_kern_tables; + FT_UInt32 kern_avail_bits; + FT_UInt32 kern_order_bits; +#else /* the format 0 kerning table, if any */ FT_Int num_kern_pairs; FT_Int kern_table_index; TT_Kern0_Pair kern_pairs; +#endif /* A pointer to the bytecode interpreter to use. This is also */ /* used to hook the debugger for the `ttdebug' utility. */ diff --git a/src/base/ftdbgmem.c b/src/base/ftdbgmem.c index 97757851f..492da7e66 100644 --- a/src/base/ftdbgmem.c +++ b/src/base/ftdbgmem.c @@ -497,8 +497,6 @@ node = *pnode; if ( node ) { - FT_MemSource source; - if ( node->size < 0 ) { /* this block was already freed. This means that our memory is */ @@ -536,7 +534,7 @@ if ( source->cur_blocks > source->max_blocks ) source->max_blocks = source->cur_blocks; - if ( size > source->cur_max ) + if ( size > (FT_ULong)source->cur_max ) source->cur_max = size; source->all_size += size; @@ -937,10 +935,10 @@ printf( "FreeType Memory Dump: current=%ld max=%ld total=%ld count=%ld\n", table->alloc_current, table->alloc_max, table->alloc_total, table->alloc_count ); - printf( " block block sizes sizes sizes source\n" ); - printf( " count high sum highsum max location\n" ); + printf( " block block sizes sizes sizes source\n" ); + printf( " count high sum highsum max location\n" ); printf( "-------------------------------------------------\n" ); - fmt = "%6ld %6ld %10ld %10ld %10ld %s:%d\n"; + fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n"; for ( ; bucket < limit; bucket++ ) { diff --git a/src/cff/cffdrivr.c b/src/cff/cffdrivr.c index b84bfdbf9..bc365ddf5 100644 --- a/src/cff/cffdrivr.c +++ b/src/cff/cffdrivr.c @@ -67,7 +67,7 @@ /*************************************************************************/ /* */ /* */ - /* Get_Kerning */ + /* cff_get_kerning */ /* */ /* */ /* A driver method used to return the kerning vector between two */ @@ -97,56 +97,21 @@ /* They can be implemented by format-specific interfaces. */ /* */ FT_CALLBACK_DEF( FT_Error ) - Get_Kerning( FT_Face ttface, /* TT_Face */ - FT_UInt left_glyph, - FT_UInt right_glyph, - FT_Vector* kerning ) + cff_get_kerning( FT_Face ttface, /* TT_Face */ + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_Vector* kerning ) { - TT_Face face = (TT_Face)ttface; - TT_Kern0_Pair pair; - - - if ( !face ) - return CFF_Err_Invalid_Face_Handle; + TT_Face face = (TT_Face)ttface; + SFNT_Service sfnt = face->sfnt; kerning->x = 0; kerning->y = 0; - if ( face->kern_pairs ) - { - /* there are some kerning pairs in this font file! */ - FT_ULong search_tag = PAIR_TAG( left_glyph, right_glyph ); - FT_Long left, right; + if ( sfnt ) + kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph ); - - left = 0; - right = face->num_kern_pairs - 1; - - while ( left <= right ) - { - FT_Long middle = left + ( ( right - left ) >> 1 ); - FT_ULong cur_pair; - - - pair = face->kern_pairs + middle; - cur_pair = PAIR_TAG( pair->left, pair->right ); - - if ( cur_pair == search_tag ) - goto Found; - - if ( cur_pair < search_tag ) - left = middle + 1; - else - right = middle - 1; - } - } - - Exit: - return CFF_Err_Ok; - - Found: - kerning->x = pair->value; - goto Exit; + return 0; } @@ -472,7 +437,7 @@ Load_Glyph, - Get_Kerning, + cff_get_kerning, 0, /* FT_Face_AttachFunc */ 0 /* FT_Face_GetAdvancesFunc */ }; diff --git a/src/sfnt/Jamfile b/src/sfnt/Jamfile index ce61d0807..256b63afd 100644 --- a/src/sfnt/Jamfile +++ b/src/sfnt/Jamfile @@ -8,7 +8,7 @@ SubDir FT2_TOP $(FT2_SRC_DIR) sfnt ; if $(FT2_MULTI) { - _sources = sfobjs sfdriver ttcmap ttpost ttload ttsbit ; + _sources = sfobjs sfdriver ttcmap ttpost ttload ttsbit ttkern ; } else { diff --git a/src/sfnt/rules.mk b/src/sfnt/rules.mk index fab5492ea..865eac1a1 100644 --- a/src/sfnt/rules.mk +++ b/src/sfnt/rules.mk @@ -29,6 +29,7 @@ SFNT_DRV_SRC := $(SFNT_DIR)/ttload.c \ $(SFNT_DIR)/ttcmap.c \ $(SFNT_DIR)/ttsbit.c \ $(SFNT_DIR)/ttpost.c \ + $(SFNT_DIR)/ttkern.c \ $(SFNT_DIR)/sfobjs.c \ $(SFNT_DIR)/sfdriver.c diff --git a/src/sfnt/sfdriver.c b/src/sfnt/sfdriver.c index 627899cd5..5d945fbbd 100644 --- a/src/sfnt/sfdriver.c +++ b/src/sfnt/sfdriver.c @@ -385,6 +385,9 @@ #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES + /* see `ttkern.h' */ + tt_face_get_kerning, + /* see `ttpost.h' */ tt_face_get_ps_name, tt_face_free_ps_names, diff --git a/src/sfnt/sfnt.c b/src/sfnt/sfnt.c index ec6dc466e..1102bcb5e 100644 --- a/src/sfnt/sfnt.c +++ b/src/sfnt/sfnt.c @@ -21,6 +21,7 @@ #include #include "ttload.c" #include "ttcmap.c" +#include "ttkern.c" #include "sfobjs.c" #include "sfdriver.c" diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c index 98bc67785..744aa846c 100644 --- a/src/sfnt/sfobjs.c +++ b/src/sfnt/sfobjs.c @@ -20,6 +20,7 @@ #include "sfobjs.h" #include "ttload.h" #include "ttcmap.h" +#include "ttkern.h" #include FT_INTERNAL_SFNT_H #include FT_TRUETYPE_IDS_H #include FT_TRUETYPE_TAGS_H @@ -503,11 +504,13 @@ #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ if ( LOAD_( hdmx ) || - LOAD_( gasp ) || - LOAD_( kerning ) || LOAD_( pclt ) ) goto Exit; + /* consider the kerning and gasp tables as optional */ + (void)LOAD_( gasp ); + (void)LOAD_( kerning ); + face->root.family_name = tt_face_get_name( face, TT_NAME_ID_PREFERRED_FAMILY ); if ( !face->root.family_name ) @@ -553,9 +556,11 @@ if ( face->vertical_info ) flags |= FT_FACE_FLAG_VERTICAL; +#if 0 /* kerning available ? */ - if ( face->kern_pairs ) + if ( TT_FACE_HAS_KERNING(face) ) flags |= FT_FACE_FLAG_KERNING; +#endif #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT /* Don't bother to load the tables unless somebody asks for them. */ @@ -802,8 +807,7 @@ } /* freeing the kerning table */ - FT_FREE( face->kern_pairs ); - face->num_kern_pairs = 0; + tt_face_done_kern( face ); /* freeing the collection table */ FT_FREE( face->ttc_header.offsets ); @@ -823,8 +827,19 @@ } /* freeing the horizontal metrics */ +#ifdef FT_OPTIMIZE_MEMORY + { + FT_Stream stream = FT_FACE_STREAM( face ); + + FT_FRAME_RELEASE( face->horz_metrics ); + FT_FRAME_RELEASE( face->vert_metrics ); + face->horz_metrics_size = 0; + face->vert_metrics_size = 0; + } +#else FT_FREE( face->horizontal.long_metrics ); FT_FREE( face->horizontal.short_metrics ); +#endif /* freeing the vertical ones, if any */ if ( face->vertical_info ) diff --git a/src/sfnt/ttkern.c b/src/sfnt/ttkern.c new file mode 100644 index 000000000..a75d56620 --- /dev/null +++ b/src/sfnt/ttkern.c @@ -0,0 +1,479 @@ +/***************************************************************************/ +/* */ +/* ttkern.h */ +/* */ +/* Load the basic TrueType kerning table. This doesn't handle */ +/* kerning data within the GPOS table at the moment. */ +/* */ +/* Copyright 1996-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 +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_STREAM_H +#include FT_TRUETYPE_TAGS_H +#include "ttload.h" + +#include "sferrors.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttload + + +#undef TT_KERN_INDEX +#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) ) + + +#ifdef FT_OPTIMIZE_MEMORY + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong table_size; + FT_Byte* p; + FT_Byte* p_limit; + FT_UInt nn, num_tables; + FT_UInt32 avail = 0, ordered = 0; + + + /* the kern table is optional; exit silently if it is missing */ + error = face->goto_table( face, TTAG_kern, stream, &table_size ); + if ( error ) + goto Exit; + + if ( table_size < 4 ) /* the case of a malformed table */ + { + FT_ERROR(( "kerning table is too small - ignored\n" )); + error = SFNT_Err_Table_Missing; + goto Exit; + } + + if ( FT_FRAME_EXTRACT( table_size, face->kern_table ) ) + { + FT_ERROR(( "could not extract kerning table\n" )); + goto Exit; + } + + face->kern_table_size = table_size; + + p = face->kern_table; + p_limit = p + table_size; + + p += 2; /* skip version */ + num_tables = FT_NEXT_USHORT(p); + + if ( num_tables > 32 ) /* we only support up to 32 sub-tables */ + num_tables = 32; + + for ( nn = 0; nn < num_tables; nn++ ) + { + FT_UInt num_pairs, version, length, coverage; + FT_Byte* p_next; + FT_UInt32 mask = 1UL << nn; + + if ( p + 6 > p_limit ) + break; + + p_next = p; + + version = FT_NEXT_USHORT(p); + length = FT_NEXT_USHORT(p); + coverage = FT_NEXT_USHORT(p); + + if ( length <= 6 ) + break; + + p_next += length; + + if ( (coverage & ~8) != 0x0001 || /* only use horizontal kerning tables */ + p+8 > p_limit ) + goto NextTable; + + num_pairs = FT_NEXT_USHORT(p); + p += 6; + + if ( p + 6*num_pairs > p_limit ) + goto NextTable; + + avail |= mask; + + /* now, try to see if the pairs in this table are ordered. + * when they are, we'll be able to use binary search + */ + if ( num_pairs > 0 ) + { + FT_UInt count; + FT_UInt old_pair; + + old_pair = FT_NEXT_ULONG(p); + p += 2; + + for ( count = num_pairs-1; count > 0; count-- ) + { + FT_UInt32 cur_pair; + + cur_pair = FT_NEXT_ULONG(p); + if ( cur_pair <= old_pair ) + break; + + p += 2; + old_pair = cur_pair; + } + + if ( count == 0 ) + ordered |= mask; + } + + NextTable: + p = p_next; + } + + face->num_kern_tables = nn; + face->kern_avail_bits = avail; + face->kern_order_bits = ordered; + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + tt_face_done_kern( TT_Face face ) + { + FT_Stream stream = face->root.stream; + + FT_FRAME_RELEASE( face->kern_table ); + face->kern_table_size = 0; + face->num_kern_tables = 0; + face->kern_avail_bits = 0; + face->kern_order_bits = 0; + } + + + FT_LOCAL_DEF( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ) + { + FT_Int result = 0; + FT_Int value; + FT_UInt count, mask = 1; + FT_Byte* p = face->kern_table; + FT_Byte* p_limit = p + face->kern_table_size; + + p += 4; + mask = 0x0001; + + for ( count = face->num_kern_tables; count > 0; count--, mask <<= 1 ) + { + FT_Byte* base = p; + FT_Byte* next = base; + FT_UInt version = FT_NEXT_USHORT(p); + FT_UInt length = FT_NEXT_USHORT(p); + FT_UInt coverage = FT_NEXT_USHORT(p); + + next = base + length; + + if ( (face->kern_avail_bits & mask) == 0 ) + goto NextTable; + + if ( p+8 > next ) + goto NextTable; + + switch ( coverage >> 8 ) + { + case 0: + { + FT_UInt num_pairs = FT_NEXT_USHORT(p); + FT_ULong key0 = TT_KERN_INDEX(left_glyph,right_glyph); + FT_Int value = 0; + + p += 6; + + if ( face->kern_order_bits & mask ) /* binary search */ + { + FT_UInt min = 0; + FT_UInt max = num_pairs; + FT_Byte* q; + + while ( min < max ) + { + FT_UInt mid = (min+max) >> 1; + FT_Byte* q = p + 6*mid; + FT_ULong key; + + key = FT_NEXT_ULONG(q); + + if ( key == key0 ) + { + value = FT_PEEK_SHORT(q); + break; + } + if ( key < key0 ) + min = mid+1; + else + max = mid; + } + } + else /* linear search */ + { + FT_UInt count = num_pairs; + + for ( ; count > 0; count-- ) + { + FT_ULong key = FT_NEXT_ULONG(p); + + if ( key == key0 ) + { + value = FT_PEEK_SHORT(p); + break; + } + p += 2; + } + } + } + break; + + /* we don't support format 2 because we've never seen a single font + * using it in real life... + */ + + default: + ; + } + + if ( coverage & 8 ) /* overide or addition */ + result = value; + else + result += value; + + NextTable: + p = next; + } + + Exit: + return result; + } + + +#else /* !OPTMIZE_MEMORY */ + + FT_CALLBACK_DEF( int ) + tt_kern_pair_compare( const void* a, + const void* b ); + + + FT_LOCAL_DEF( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + + FT_UInt n, num_tables; + + + /* the kern table is optional; exit silently if it is missing */ + error = face->goto_table( face, TTAG_kern, stream, 0 ); + if ( error ) + return SFNT_Err_Ok; + + if ( FT_FRAME_ENTER( 4L ) ) + goto Exit; + + (void)FT_GET_USHORT(); /* version */ + num_tables = FT_GET_USHORT(); + + FT_FRAME_EXIT(); + + for ( n = 0; n < num_tables; n++ ) + { + FT_UInt coverage; + FT_UInt length; + + + if ( FT_FRAME_ENTER( 6L ) ) + goto Exit; + + (void)FT_GET_USHORT(); /* version */ + length = FT_GET_USHORT() - 6; /* substract header length */ + coverage = FT_GET_USHORT(); + + FT_FRAME_EXIT(); + + if ( coverage == 0x0001 ) + { + FT_UInt num_pairs; + TT_Kern0_Pair pair; + TT_Kern0_Pair limit; + + + /* found a horizontal format 0 kerning table! */ + if ( FT_FRAME_ENTER( 8L ) ) + goto Exit; + + num_pairs = FT_GET_USHORT(); + + /* skip the rest */ + + FT_FRAME_EXIT(); + + /* allocate array of kerning pairs */ + if ( FT_QNEW_ARRAY( face->kern_pairs, num_pairs ) || + FT_FRAME_ENTER( 6L * num_pairs ) ) + goto Exit; + + pair = face->kern_pairs; + limit = pair + num_pairs; + for ( ; pair < limit; pair++ ) + { + pair->left = FT_GET_USHORT(); + pair->right = FT_GET_USHORT(); + pair->value = FT_GET_USHORT(); + } + + FT_FRAME_EXIT(); + + face->num_kern_pairs = num_pairs; + face->kern_table_index = n; + + /* ensure that the kerning pair table is sorted (yes, some */ + /* fonts have unsorted tables!) */ + + if ( num_pairs > 0 ) + { + TT_Kern0_Pair pair0 = face->kern_pairs; + FT_ULong prev = TT_KERN_INDEX( pair0->left, pair0->right ); + + + for ( pair0++; pair0 < limit; pair0++ ) + { + FT_ULong next = TT_KERN_INDEX( pair0->left, pair0->right ); + + + if ( next < prev ) + goto SortIt; + + prev = next; + } + goto Exit; + + SortIt: + ft_qsort( (void*)face->kern_pairs, (int)num_pairs, + sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare ); + } + + goto Exit; + } + + if ( FT_STREAM_SKIP( length ) ) + goto Exit; + } + + /* no kern table found -- doesn't matter */ + face->kern_table_index = -1; + face->num_kern_pairs = 0; + face->kern_pairs = NULL; + + Exit: + return error; + } + + FT_CALLBACK_DEF( int ) + tt_kern_pair_compare( const void* a, + const void* b ) + { + TT_Kern0_Pair pair1 = (TT_Kern0_Pair)a; + TT_Kern0_Pair pair2 = (TT_Kern0_Pair)b; + + FT_ULong index1 = TT_KERN_INDEX( pair1->left, pair1->right ); + FT_ULong index2 = TT_KERN_INDEX( pair2->left, pair2->right ); + + + return ( index1 < index2 ? -1 : + ( index1 > index2 ? 1 : 0 )); + } + + + FT_LOCAL_DEF( void ) + tt_face_done_kern( TT_Face face ) + { + FT_Memory memory = face->root.stream->memory; + + FT_FREE( face->kern_pairs ); + face->num_kern_pairs = 0; + } + + + + FT_LOCAL_DEF( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ) + { + FT_Int result = 0; + TT_Kern0_Pair pair; + + + if ( face && face->kern_pairs ) + { + /* there are some kerning pairs in this font file! */ + FT_ULong search_tag = TT_KERN_INDEX( left_glyph, right_glyph ); + FT_Long left, right; + + + left = 0; + right = face->num_kern_pairs - 1; + + while ( left <= right ) + { + FT_Long middle = left + ( ( right - left ) >> 1 ); + FT_ULong cur_pair; + + + pair = face->kern_pairs + middle; + cur_pair = TT_KERN_INDEX( pair->left, pair->right ); + + if ( cur_pair == search_tag ) + goto Found; + + if ( cur_pair < search_tag ) + left = middle + 1; + else + right = middle - 1; + } + } + + Exit: + return result; + + Found: + result = pair->value; + goto Exit; + } + +#endif /* !OPTIMIZE_MEMORY */ + + +#undef TT_KERN_INDEX + +/* END */ diff --git a/src/sfnt/ttkern.h b/src/sfnt/ttkern.h new file mode 100644 index 000000000..f71849b1f --- /dev/null +++ b/src/sfnt/ttkern.h @@ -0,0 +1,55 @@ +/***************************************************************************/ +/* */ +/* ttkern.h */ +/* */ +/* Load the basic TrueType kerning table. This doesn't handle */ +/* kerning data within the GPOS table at the moment. */ +/* */ +/* Copyright 1996-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. */ +/* */ +/***************************************************************************/ + + +#ifndef __TTKERN_H__ +#define __TTKERN_H__ + + +#include +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_TRUETYPE_TYPES_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + tt_face_load_kern( TT_Face face, + FT_Stream stream ); + + FT_LOCAL( void ) + tt_face_done_kern( TT_Face face ); + + FT_LOCAL( FT_Int ) + tt_face_get_kerning( TT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph ); + +#ifdef FT_OPTIMIZE_MEMORY +# define TT_FACE_HAS_KERNING(face) ((face)->kern_avail_bits != 0) +#else +# define TT_FACE_HAS_KERNING(face) ((face)->kern_pairs != NULL) +#endif + +FT_END_HEADER + +#endif /* __TTKERN_H__ */ + + +/* END */ diff --git a/src/sfnt/ttload.c b/src/sfnt/ttload.c index 1a3dcb6d2..e7a45f7e0 100644 --- a/src/sfnt/ttload.c +++ b/src/sfnt/ttload.c @@ -800,6 +800,82 @@ /* */ /* FreeType error code. 0 means success. */ /* */ +#ifdef FT_OPTIMIZE_MEMORY + static FT_Error + tt_face_load_metrics( TT_Face face, + FT_Stream stream, + FT_Bool vertical ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_ULong table_size; + FT_Byte** ptable; + FT_ULong* ptable_size; + + + FT_TRACE2(( "TT_Load_%s_Metrics: %08p\n", vertical ? "Vertical" + : "Horizontal", + face )); + + if ( vertical ) + { + ptable = &face->vert_metrics; + ptable_size = &face->vert_metrics_size; + + /* The table is optional, quit silently if it wasn't found */ + /* */ + /* XXX: Some fonts have a valid vertical header with a non-null */ + /* `number_of_VMetrics' fields, but no corresponding `vmtx' */ + /* table to get the metrics from (e.g. mingliu). */ + /* */ + /* For safety, we set the field to 0! */ + /* */ + error = face->goto_table( face, TTAG_vmtx, stream, &table_size ); + if ( error ) + { + /* Set number_Of_VMetrics to 0! */ + FT_TRACE2(( " no vertical header in file.\n" )); + error = SFNT_Err_Ok; + goto Exit; + } + } + else + { + ptable = &face->horz_metrics; + ptable_size = &face->horz_metrics_size; + + error = face->goto_table( face, TTAG_hmtx, stream, &table_size ); + if ( error ) + { +#ifdef FT_CONFIG_OPTION_INCREMENTAL + /* If this is an incrementally loaded font and there are */ + /* overriding metrics tolerate a missing 'hmtx' table. */ + if ( face->root.internal->incremental_interface && + face->root.internal->incremental_interface->funcs-> + get_glyph_metrics ) + { + face->horizontal.number_Of_HMetrics = 0; + error = SFNT_Err_Ok; + goto Exit; + } +#endif + + FT_ERROR(( " no horizontal metrics in file!\n" )); + error = SFNT_Err_Hmtx_Table_Missing; + goto Exit; + } + } + + if ( FT_FRAME_EXTRACT( table_size, *ptable ) ) + goto Exit; + + *ptable_size = table_size; + + Exit: + return error; + } + +#else /* !OPTIMIZE_MEMORY */ static FT_Error tt_face_load_metrics( TT_Face face, FT_Stream stream, @@ -938,7 +1014,7 @@ Exit: return error; } - +#endif /* !FT_OPTIMIZE_METRICS */ /*************************************************************************/ /* */ @@ -1611,190 +1687,6 @@ } - FT_CALLBACK_DEF( int ) - tt_kern_pair_compare( const void* a, - const void* b ); - - - /*************************************************************************/ - /* */ - /* */ - /* tt_face_load_kern */ - /* */ - /* */ - /* Loads the first kerning table with format 0 in the font. Only */ - /* accepts the first horizontal kerning table. Developers should use */ - /* the `ftxkern' extension to access other kerning tables in the font */ - /* file, if they really want to. */ - /* */ - /* */ - /* face :: A handle to the target face object. */ - /* */ - /* stream :: The input stream. */ - /* */ - /* */ - /* FreeType error code. 0 means success. */ - /* */ - -#undef TT_KERN_INDEX -#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 ) - - - FT_LOCAL_DEF( FT_Error ) - tt_face_load_kern( TT_Face face, - FT_Stream stream ) - { - FT_Error error; - FT_Memory memory = stream->memory; - - FT_UInt n, num_tables; - - - /* the kern table is optional; exit silently if it is missing */ - error = face->goto_table( face, TTAG_kern, stream, 0 ); - if ( error ) - return SFNT_Err_Ok; - - if ( FT_FRAME_ENTER( 4L ) ) - goto Exit; - - (void)FT_GET_USHORT(); /* version */ - num_tables = FT_GET_USHORT(); - - FT_FRAME_EXIT(); - - for ( n = 0; n < num_tables; n++ ) - { - FT_UInt coverage; - FT_UInt length; - - - if ( FT_FRAME_ENTER( 6L ) ) - goto Exit; - - (void)FT_GET_USHORT(); /* version */ - length = FT_GET_USHORT() - 6; /* substract header length */ - coverage = FT_GET_USHORT(); - - FT_FRAME_EXIT(); - - if ( coverage == 0x0001 ) - { - FT_UInt num_pairs; - TT_Kern0_Pair pair; - TT_Kern0_Pair limit; - - - /* found a horizontal format 0 kerning table! */ - if ( FT_FRAME_ENTER( 8L ) ) - goto Exit; - - num_pairs = FT_GET_USHORT(); - - /* skip the rest */ - - FT_FRAME_EXIT(); - - /* allocate array of kerning pairs */ - if ( FT_QNEW_ARRAY( face->kern_pairs, num_pairs ) || - FT_FRAME_ENTER( 6L * num_pairs ) ) - goto Exit; - - pair = face->kern_pairs; - limit = pair + num_pairs; - for ( ; pair < limit; pair++ ) - { - pair->left = FT_GET_USHORT(); - pair->right = FT_GET_USHORT(); - pair->value = FT_GET_USHORT(); - } - - FT_FRAME_EXIT(); - - face->num_kern_pairs = num_pairs; - face->kern_table_index = n; - - /* ensure that the kerning pair table is sorted (yes, some */ - /* fonts have unsorted tables!) */ - -#if 1 - if ( num_pairs > 0 ) - { - TT_Kern0_Pair pair0 = face->kern_pairs; - FT_ULong prev = TT_KERN_INDEX( pair0->left, pair0->right ); - - - for ( pair0++; pair0 < limit; pair0++ ) - { - FT_ULong next = TT_KERN_INDEX( pair0->left, pair0->right ); - - - if ( next < prev ) - goto SortIt; - - prev = next; - } - goto Exit; - - SortIt: - ft_qsort( (void*)face->kern_pairs, (int)num_pairs, - sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare ); - } -#else - { - TT_Kern0_Pair pair0 = face->kern_pairs; - FT_UInt i; - - - for ( i = 1; i < num_pairs; i++, pair0++ ) - { - if ( tt_kern_pair_compare( pair0, pair0 + 1 ) != -1 ) - { - ft_qsort( (void*)face->kern_pairs, (int)num_pairs, - sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare ); - break; - } - } - } -#endif - - goto Exit; - } - - if ( FT_STREAM_SKIP( length ) ) - goto Exit; - } - - /* no kern table found -- doesn't matter */ - face->kern_table_index = -1; - face->num_kern_pairs = 0; - face->kern_pairs = NULL; - - Exit: - return error; - } - - - FT_CALLBACK_DEF( int ) - tt_kern_pair_compare( const void* a, - const void* b ) - { - TT_Kern0_Pair pair1 = (TT_Kern0_Pair)a; - TT_Kern0_Pair pair2 = (TT_Kern0_Pair)b; - - FT_ULong index1 = TT_KERN_INDEX( pair1->left, pair1->right ); - FT_ULong index2 = TT_KERN_INDEX( pair2->left, pair2->right ); - - - return ( index1 < index2 ? -1 : - ( index1 > index2 ? 1 : 0 )); - } - - -#undef TT_KERN_INDEX - - - /*************************************************************************/ /* */ /* */ @@ -1811,6 +1703,75 @@ /* */ /* FreeType error code. 0 means success. */ /* */ +#ifdef FT_OPTIMIZE_MEMORY + FT_LOCAL_DEF( FT_Error ) + tt_face_load_hdmx( TT_Face face, + FT_Stream stream ) + { + FT_Error error; + FT_Memory memory = stream->memory; + FT_UInt version, nn, num_records; + FT_ULong table_size, record_size; + FT_Byte* p; + FT_Byte* limit; + + /* this table is optional */ + error = face->goto_table( face, TTAG_hdmx, stream, &table_size ); + if ( error || table_size < 8 ) + return SFNT_Err_Ok; + + if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) ) + goto Exit; + + p = face->hdmx_table; + limit = p + table_size; + + version = FT_NEXT_USHORT(p); + num_records = FT_NEXT_USHORT(p); + record_size = FT_NEXT_ULONG(p); + + if ( version != 0 || num_records > 255 || record_size > 0x40000 ) + { + error = SFNT_Err_Invalid_File_Format; + goto Fail; + } + + if ( FT_NEW_ARRAY( face->hdmx_record_sizes, num_records ) ) + goto Fail; + + for ( nn = 0; nn < num_records; nn++ ) + { + if ( p+record_size > limit ) + break; + + face->hdmx_record_sizes[nn] = p[0]; + p += record_size; + } + + face->hdmx_record_count = nn; + face->hdmx_table_size = table_size; + + Exit: + return error; + + Fail: + FT_FRAME_RELEASE( face->hdmx_table ); + face->hdmx_table_size = 0; + goto Exit; + } + + + FT_LOCAL_DEF( void ) + tt_face_free_hdmx( TT_Face face ) + { + FT_Stream stream = face->root.stream; + FT_Memory memory = stream->memory; + + FT_FREE( face->hdmx_record_sizes ); + FT_FRAME_RELEASE( face->hdmx_table ); + } + +#else /* !FT_OPTIMIZE_MEMORY */ FT_LOCAL_DEF( FT_Error ) tt_face_load_hdmx( TT_Face face, FT_Stream stream ) @@ -1885,17 +1846,6 @@ } - /*************************************************************************/ - /* */ - /* */ - /* tt_face_free_hdmx */ - /* */ - /* */ - /* Frees the horizontal device metrics table. */ - /* */ - /* */ - /* face :: A handle to the target face object. */ - /* */ FT_LOCAL_DEF( void ) tt_face_free_hdmx( TT_Face face ) { @@ -1913,5 +1863,7 @@ } } +#endif /* !OPTIMIZE_MEMORY */ + /* END */ diff --git a/src/sfnt/ttload.h b/src/sfnt/ttload.h index 27c41b50b..439b4df3e 100644 --- a/src/sfnt/ttload.h +++ b/src/sfnt/ttload.h @@ -111,11 +111,6 @@ FT_BEGIN_HEADER tt_face_free_hdmx ( TT_Face face ); - FT_LOCAL( FT_Error ) - tt_face_load_kern( TT_Face face, - FT_Stream stream ); - - FT_LOCAL( FT_Error ) tt_face_load_gasp( TT_Face face, FT_Stream stream ); diff --git a/src/sfnt/ttsbit.c b/src/sfnt/ttsbit.c index ea48bde51..65b3bad93 100644 --- a/src/sfnt/ttsbit.c +++ b/src/sfnt/ttsbit.c @@ -185,7 +185,7 @@ } - const FT_Frame_Field sbit_metrics_fields[] = + static const FT_Frame_Field sbit_metrics_fields[] = { #undef FT_STRUCTURE #define FT_STRUCTURE TT_SBit_MetricsRec diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c index 25948839d..0e0396ddc 100644 --- a/src/truetype/ttdriver.c +++ b/src/truetype/ttdriver.c @@ -69,7 +69,7 @@ /*************************************************************************/ /* */ /* */ - /* Get_Kerning */ + /* tt_get_kerning */ /* */ /* */ /* A driver method used to return the kerning vector between two */ @@ -99,56 +99,21 @@ /* They can be implemented by format-specific interfaces. */ /* */ static FT_Error - Get_Kerning( FT_Face ttface, /* TT_Face */ + tt_get_kerning( FT_Face ttface, /* TT_Face */ FT_UInt left_glyph, FT_UInt right_glyph, FT_Vector* kerning ) { - TT_Face face = (TT_Face)ttface; - TT_Kern0_Pair pair; - - - if ( !face ) - return TT_Err_Invalid_Face_Handle; + TT_Face face = (TT_Face)ttface; + SFNT_Service sfnt = (SFNT_Service) face->sfnt; kerning->x = 0; kerning->y = 0; - if ( face->kern_pairs ) - { - /* there are some kerning pairs in this font file! */ - FT_ULong search_tag = PAIR_TAG( left_glyph, right_glyph ); - FT_Long left, right; - - - left = 0; - right = face->num_kern_pairs - 1; - - while ( left <= right ) - { - FT_Long middle = left + ( ( right - left ) >> 1 ); - FT_ULong cur_pair; - - - pair = face->kern_pairs + middle; - cur_pair = PAIR_TAG( pair->left, pair->right ); - - if ( cur_pair == search_tag ) - goto Found; - - if ( cur_pair < search_tag ) - left = middle + 1; - else - right = middle - 1; - } - } - - Exit: - return TT_Err_Ok; - - Found: - kerning->x = pair->value; - goto Exit; + if ( sfnt ) + kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph ); + + return 0; } @@ -456,7 +421,7 @@ Set_Pixel_Sizes, Load_Glyph, - Get_Kerning, + tt_get_kerning, 0, /* FT_Face_AttachFunc */ 0 /* FT_Face_GetAdvancesFunc */ }; diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c index 036a7d2ed..ddf4c2afd 100644 --- a/src/truetype/ttgload.c +++ b/src/truetype/ttgload.c @@ -96,34 +96,99 @@ /* This function will much probably move to another component in the */ /* near future, but I haven't decided which yet. */ /* */ - FT_LOCAL_DEF( void ) - TT_Get_Metrics( TT_HoriHeader* header, - FT_UInt idx, - FT_Short* bearing, - FT_UShort* advance ) +#ifdef FT_OPTIMIZE_MEMORY + static void + tt_face_get_metrics( TT_Face face, + FT_Bool vertical, + FT_UInt idx, + FT_Short *abearing, + FT_UShort *aadvance ) { + TT_HoriHeader* header; + FT_Byte* p; + FT_Byte* limit; + FT_UShort k; + + if ( vertical ) + { + header = (TT_HoriHeader*)&face->vertical; + p = face->vert_metrics; + limit = p + face->vert_metrics_size; + } + else + { + header = &face->horizontal; + p = face->horz_metrics; + limit = p + face->horz_metrics_size; + } + + k = header->number_Of_HMetrics; + + if ( k > 0 ) + { + if ( idx < (FT_UInt)k ) + { + p += 4*idx; + if ( p+4 >= limit ) + goto NoData; + + *aadvance = FT_NEXT_USHORT(p); + *abearing = FT_NEXT_SHORT(p); + } + else + { + p += 4*(k-1); + if ( p+4 > limit ) + goto NoData; + + *aadvance = FT_NEXT_USHORT(p); + p += 2 + 2*(idx-k); + if ( p+2 > limit ) + *abearing = 0; + else + *abearing = FT_PEEK_SHORT(p); + } + } + else + { + NoData: + *abearing = 0; + *aadvance = 0; + } + } +#else + static void + tt_face_get_metrics( TT_Face face, + FT_Bool vertical, + FT_UInt idx, + FT_Short *abearing, + FT_UShort *aadvance ) + { + TT_HoriHeader* header = (vertical ? (TT_HoriHeader*)&face->vertical + : &face->horizontal); TT_LongMetrics longs_m; - FT_UShort k = header->number_Of_HMetrics; + FT_UShort k = header->number_Of_HMetrics; if ( k == 0 ) { - *bearing = *advance = 0; + *abearing = *aadvance = 0; return; } if ( idx < (FT_UInt)k ) { - longs_m = (TT_LongMetrics )header->long_metrics + idx; - *bearing = longs_m->bearing; - *advance = longs_m->advance; + longs_m = (TT_LongMetrics )header->long_metrics + idx; + *abearing = longs_m->bearing; + *aadvance = longs_m->advance; } else { - *bearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k]; - *advance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance; + *abearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k]; + *aadvance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance; } } +#endif /*************************************************************************/ @@ -139,7 +204,7 @@ FT_Short* lsb, FT_UShort* aw ) { - TT_Get_Metrics( &face->horizontal, idx, lsb, aw ); + tt_face_get_metrics( face, 0, idx, lsb, aw ); if ( check && face->postscript.isFixedPitch ) *aw = face->horizontal.advance_Width_Max; @@ -152,17 +217,36 @@ /* in the font's `hdmx' table (if any). */ /* */ static FT_Byte* - Get_Advance_Widths( TT_Face face, - FT_UShort ppem ) + Get_Advance_WidthPtr( TT_Face face, + FT_Int ppem, + FT_UInt gindex ) { +#ifdef FT_OPTIMIZE_MEMORY + FT_UInt nn; + FT_Byte* result = NULL; + FT_ULong record_size = face->hdmx_record_size; + FT_Byte* record = face->hdmx_table + 8; + + for ( nn = 0; nn < face->hdmx_record_count; nn++ ) + if ( face->hdmx_record_sizes[nn] == ppem ) + { + gindex += 2; + if ( gindex < record_size ) + result = record + gindex; + break; + } + + return result; +#else FT_UShort n; for ( n = 0; n < face->hdmx.num_records; n++ ) if ( face->hdmx.records[n].ppem == ppem ) - return face->hdmx.records[n].widths; + return &face->hdmx.records[n].widths[gindex]; return NULL; +#endif } @@ -189,7 +273,7 @@ FT_UNUSED( check ); if ( face->vertical_info ) - TT_Get_Metrics( (TT_HoriHeader *)&face->vertical, idx, tsb, ah ); + tt_face_get_metrics( face, 1, idx, tsb, ah ); #if 1 /* Emperically determined, at variance with what MS said */ @@ -1782,12 +1866,13 @@ if ( !face->postscript.isFixedPitch && size && IS_HINTED( loader->load_flags ) ) { - FT_Byte* widths = Get_Advance_Widths( face, - size->root.metrics.x_ppem ); + FT_Byte* widthp = Get_Advance_WidthPtr( face, + size->root.metrics.x_ppem, + glyph_index ); - if ( widths ) - glyph->metrics.horiAdvance = widths[glyph_index] << 6; + if ( widthp ) + glyph->metrics.horiAdvance = *widthp << 6; } /* set glyph dimensions */ diff --git a/src/truetype/ttgload.h b/src/truetype/ttgload.h index dfa2a60a5..27565ed63 100644 --- a/src/truetype/ttgload.h +++ b/src/truetype/ttgload.h @@ -31,12 +31,6 @@ FT_BEGIN_HEADER - FT_LOCAL( void ) - TT_Get_Metrics( TT_HoriHeader* header, - FT_UInt idx, - FT_Short* bearing, - FT_UShort* advance ); - FT_LOCAL( void ) TT_Init_Glyph_Loading( TT_Face face );