* 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...
This commit is contained in:
David Turner 2005-02-26 00:12:04 +00:00
parent d7fe752715
commit a511bc8b9b
18 changed files with 903 additions and 334 deletions

View File

@ -1,3 +1,20 @@
2005-02-25 David Turner <david@freetype.org>
* 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 <david@freetype.org>
* src/base/ftdbgmem.c: adding the ability to list all allocation sites

View File

@ -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;

View File

@ -310,7 +310,7 @@ FT_BEGIN_HEADER
} TT_GaspRec;
#ifndef FT_OPTIMIZE_MEMORY
/*************************************************************************/
/* */
/* <Struct> */
@ -360,8 +360,6 @@ FT_BEGIN_HEADER
} TT_HdmxRec, *TT_Hdmx;
/*************************************************************************/
/* */
/* <Struct> */
@ -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. */

View File

@ -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++ )
{

View File

@ -67,7 +67,7 @@
/*************************************************************************/
/* */
/* <Function> */
/* Get_Kerning */
/* cff_get_kerning */
/* */
/* <Description> */
/* 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 */
};

View File

@ -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
{

View File

@ -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

View File

@ -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,

View File

@ -21,6 +21,7 @@
#include <ft2build.h>
#include "ttload.c"
#include "ttcmap.c"
#include "ttkern.c"
#include "sfobjs.c"
#include "sfdriver.c"

View File

@ -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 )

479
src/sfnt/ttkern.c Normal file
View File

@ -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 <ft2build.h>
#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 */

55
src/sfnt/ttkern.h Normal file
View File

@ -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 <ft2build.h>
#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 */

View File

@ -800,6 +800,82 @@
/* <Return> */
/* 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 );
/*************************************************************************/
/* */
/* <Function> */
/* tt_face_load_kern */
/* */
/* <Description> */
/* 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. */
/* */
/* <Input> */
/* face :: A handle to the target face object. */
/* */
/* stream :: The input stream. */
/* */
/* <Return> */
/* 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
/*************************************************************************/
/* */
/* <Function> */
@ -1811,6 +1703,75 @@
/* <Return> */
/* 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 @@
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_face_free_hdmx */
/* */
/* <Description> */
/* Frees the horizontal device metrics table. */
/* */
/* <Input> */
/* 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 */

View File

@ -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 );

View File

@ -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

View File

@ -69,7 +69,7 @@
/*************************************************************************/
/* */
/* <Function> */
/* Get_Kerning */
/* tt_get_kerning */
/* */
/* <Description> */
/* 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 */
};

View File

@ -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 */

View File

@ -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 );