blob: 105c142187eb13a4f6a09f4a1f2664f0110dd86e [file] [log] [blame]
/***************************************************************************/
/* */
/* gxaccess.c */
/* */
/* AAT/TrueTypeGX private data accessor implementation(body). */
/* */
/* Copyright 2003 by */
/* Masatake YAMATO and Redhat K.K. */
/* */
/* This file 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. */
/* */
/***************************************************************************/
/***************************************************************************/
/* Development of the code in this file is support of */
/* Information-technology Promotion Agency, Japan. */
/***************************************************************************/
#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H
#include "gxlookuptbl.h"
#include "gxstatetbl.h"
#include "gxutils.h"
#include "gxaccess.h"
#include "gxobjs.h"
#include "gxerrors.h"
#include "gxvm.h"
#include "gxltypes.h"
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** Features ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
FT_LOCAL_DEF ( FT_Bool )
gx_feat_has_feature_type ( GX_Feat feat, FT_UShort feature_type )
{
FT_Int i;
for ( i = 0; i < feat->featureNameCount; i++ )
{
if ( feat->names[i].feature == feature_type )
return 1;
}
return 0;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** Glyph Properties ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
FT_LOCAL_DEF ( FT_UShort )
gx_prop_get( GX_Prop prop, FT_Long glyph )
{
GX_LookupTable lookup_table = &prop->lookup_data;
FT_UShort default_properties = prop->default_properties;
FT_UShort properties = 0;
GX_LookupResultRec result;
FT_UShort * segment_array;
FT_Long index_in_segment;
result = gx_LookupTable_lookup( lookup_table, glyph );
if ( result.value == NULL )
properties = default_properties;
else if ( result.firstGlyph == GX_LOOKUP_RESULT_NO_FIRST_GLYPH )
properties = result.value->raw.s;
else
{
index_in_segment = glyph - result.firstGlyph;
segment_array = result.value->extra.word;
properties = segment_array[index_in_segment];
}
return properties;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** Ligature Carret ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
FT_LOCAL_DEF ( GX_LigCaretClassEntry )
gx_lcar_get ( GX_Lcar lcar, FT_UShort glyphID )
{ /* TODO: We could put cache mechanism here. */
GX_LookupResultRec result;
result = gx_LookupTable_lookup ( &lcar->lookup, glyphID );
if ( result.value == NULL )
return NULL;
else if ( result.firstGlyph == GX_LOOKUP_RESULT_NO_FIRST_GLYPH )
return result.value->extra.lcar_class_entry;
else
return &result.value->extra.lcar_segment->class_entry[glyphID - result.firstGlyph];
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** Glyph Metamorphosis ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
#undef FT_COMPONENT
#define FT_COMPONENT trace_gxchain
FT_LOCAL_DEF ( FT_Error )
gx_mort_foreach_feature ( GX_Mort mort, GX_Mort_Feature_Func func, FT_Pointer user )
{
FT_Error error = GX_Err_Ok;
FT_Int i, j;
GX_MetamorphosisChain chain;
GX_MetamorphosisFeatureTable feat_Subtbl;
for ( i = 0; i < mort->nChains; i++ )
{
chain = &mort->chain[i];
for ( j = 0; j < chain->header.nFeatureEntries; j++ )
{
feat_Subtbl = &chain->feat_Subtbl[j];
if (( error = func ( feat_Subtbl, user ) ))
return error;
}
}
return error;
}
#define GX_MORT_COUNT_FEAT_DATA_ZERO {0, NULL}
typedef struct gx_mort_count_feat_data_rec_
{
FT_UShort count;
GX_Feat feat;
} gx_mort_count_feat_data_rec, *gx_mort_count_feat_data;
static FT_Error
gx_mort_count_feat_not_in_feat_cb ( GX_MetamorphosisFeatureTable feat_Subtbl, FT_Pointer user )
{
gx_mort_count_feat_data data = user;
FT_UShort featureType = feat_Subtbl->featureType;
if ( !gx_feat_has_feature_type ( data->feat, featureType ) )
data->count++;
return GX_Err_Ok;
}
FT_LOCAL_DEF ( FT_UShort )
gx_mort_count_feat_not_in_feat ( GX_Mort mort, GX_Feat feat )
{
gx_mort_count_feat_data_rec data;
data.count = 0;
data.feat = feat;
gx_mort_foreach_feature ( mort, gx_mort_count_feat_not_in_feat_cb, &data );
return data.count;
}
static FT_ULong
gx_chain_calc_selector ( GX_MetamorphosisChain chain, GXL_FeaturesRequest request)
{
FT_ULong j_features;
FT_ULong result;
GX_MetamorphosisFeatureTable feat_Subtbl;
GXL_Feature feature;
result = chain->header.defaultFlags;
for ( j_features = 0; j_features < chain->header.nFeatureEntries; j_features++ )
{
feat_Subtbl = &chain->feat_Subtbl[j_features];
feature = gxl_features_request_get_feature_by_type(request,
feat_Subtbl->featureType);
if ( !feature )
continue ;
if ( gxl_feature_get_setting_by_value(feature,
feat_Subtbl->featureSetting ) )
{
result &= feat_Subtbl->disableFlags;
result |= feat_Subtbl->enableFlags ;
}
}
return result;
}
FT_LOCAL_DEF( FT_Error )
gx_mort_substitute_glyph ( GX_Mort mort,
GXL_FeaturesRequest request,
FTL_GlyphArray in,
FTL_GlyphArray out )
{
FT_Error error = GX_Err_Ok;
GX_MetamorphosisChain chain;
FT_ULong i_chain, k_subtbl;
FT_ULong selector;
GX_MetamorphosisSubtable chain_Subtbl;
FT_UShort coverage;
FT_UShort subtable_type;
GXL_Order order;
if (( error = FTL_Copy_Glyphs_Array ( in, out ) ))
return error;
for ( i_chain = 0; i_chain < mort->nChains; i_chain++ )
{
chain = &mort->chain[i_chain];
selector = gx_chain_calc_selector( chain, request );
FT_TRACE2(( "Mort Chain No.%d, Address 0x%p\n", i_chain, chain ));
for ( k_subtbl = 0; k_subtbl < chain->header.nSubtables; k_subtbl++ )
{
chain_Subtbl = &chain->chain_Subtbl[k_subtbl];
if ( !(chain_Subtbl->header.subFeatureFlags & selector) )
continue;
coverage = chain_Subtbl->header.coverage;
if ( ( !(coverage & GX_MORT_COVERAGE_ORIENTATION_INDEPENDENT) )
/* Orientation dependent */
&& ((( coverage & GX_MORT_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
/* Vertical only */
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_VERTICAL)
/* But the request is NOT vertical */)
|| (!( coverage & GX_MORT_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
/* Horizontal only */
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_HORIZONTAL)
/* But the request is NOT horizontal */)))
continue;
order = (coverage & GX_MORT_COVERAGE_ORDER_OF_PROCESSING_GLYPH_ARRAY)
? GXL_DESCENDING
: GXL_ASCENDING;
if ( order == GXL_DESCENDING )
gx_glyphs_array_reverse( out->glyphs, out->length );
subtable_type = coverage & GX_MORT_COVERAGE_SUBTABLE_TYPE;
FT_TRACE2(( "\tSubtable No.%d, Address 0x%p, Type %d\n",
k_subtbl, chain_Subtbl, subtable_type ));
switch ( subtable_type )
{
case GX_MORT_REARRANGEMENT_SUBTABLE:
gx_rearrangement_subst( chain_Subtbl->body.rearrangement,
request->initial_state,
out );
break;
case GX_MORT_CONTEXTUAL_SUBTABLE:
error = gx_contextual_subst( chain_Subtbl->body.contextual,
request->initial_state,
out );
break;
case GX_MORT_LIGATURE_SUBTABLE:
error = gx_ligature_subst( chain_Subtbl->body.ligature,
request->initial_state,
out );
break;
case GX_MORT_RESERVED_SUBTABLE:
FT_ERROR(("Reserved\n"));
break;
case GX_MORT_NONCONTEXTUAL_SUBTABLE:
error = gx_xnoncontextual_subst( chain_Subtbl->body.noncontextual, out );
break;
case GX_MORT_INSERTION_SUBTABLE:
error = gx_insertion_subst( chain_Subtbl->body.insertion,
request->initial_state,
out );
break;
}
if ( order == GXL_DESCENDING )
gx_glyphs_array_reverse( out->glyphs, out->length );
}
}
return error;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** Extended Glyph Metamorphosis ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
FT_LOCAL_DEF ( FT_Error )
gx_morx_foreach_feature ( GX_Morx morx, GX_Morx_Feature_Func func, FT_Pointer user )
{
FT_Error error = GX_Err_Ok;
FT_Int i, j;
GX_XMetamorphosisChain chain;
GX_XMetamorphosisFeatureTable feat_Subtbl;
for ( i = 0; i < morx->nChains; i++ )
{
chain = &morx->chain[i];
for ( j = 0; j < chain->header.nFeatureEntries; j++ )
{
feat_Subtbl = &chain->feat_Subtbl[j];
if (( error = func ( feat_Subtbl, user ) ))
return error;
}
}
return error;
}
#define GX_MORT_COUNT_FEAT_DATA_ZERO {0, NULL}
typedef struct gx_morx_count_feat_data_rec_
{
FT_UShort count;
GX_Feat feat;
} gx_morx_count_feat_data_rec, *gx_morx_count_feat_data;
static FT_Error
gx_morx_count_feat_not_in_feat_cb ( GX_MetamorphosisFeatureTable feat_Subtbl, FT_Pointer user )
{
gx_morx_count_feat_data data = user;
FT_UShort featureType = feat_Subtbl->featureType;
if ( !gx_feat_has_feature_type ( data->feat, featureType ) )
data->count++;
return GX_Err_Ok;
}
FT_LOCAL_DEF ( FT_UShort )
gx_morx_count_feat_not_in_feat ( GX_Morx morx, GX_Feat feat )
{
gx_morx_count_feat_data_rec data;
data.count = 0;
data.feat = feat;
gx_morx_foreach_feature ( morx, gx_morx_count_feat_not_in_feat_cb, &data );
return data.count;
}
static FT_ULong
gx_xchain_calc_selector ( GX_XMetamorphosisChain chain, GXL_FeaturesRequest request)
{
FT_ULong j_features;
FT_ULong result;
GX_MetamorphosisFeatureTable feat_Subtbl;
GXL_Feature feature;
result = chain->header.defaultFlags;
for ( j_features = 0; j_features < chain->header.nFeatureEntries; j_features++ )
{
feat_Subtbl = &chain->feat_Subtbl[j_features];
feature = gxl_features_request_get_feature_by_type(request,
feat_Subtbl->featureType);
if ( !feature )
continue ;
if ( gxl_feature_get_setting_by_value(feature,
feat_Subtbl->featureSetting ) )
{
result &= feat_Subtbl->disableFlags;
result |= feat_Subtbl->enableFlags;
}
}
return result;
}
FT_LOCAL_DEF( FT_Error )
gx_morx_substitute_glyph ( GX_Morx morx,
GXL_FeaturesRequest request,
FTL_GlyphArray in,
FTL_GlyphArray out )
{
FT_Error error = GX_Err_Ok;
GX_XMetamorphosisChain xchain;
FT_ULong i_chain, k_subtbl;
FT_ULong selector;
GX_XMetamorphosisSubtable xchain_Subtbl;
FT_UShort coverage;
FT_UShort subtable_type;
GXL_Order order;
if (( error = FTL_Copy_Glyphs_Array ( in, out ) ))
return error;
for ( i_chain = 0; i_chain < morx->nChains; i_chain++ )
{
xchain = &morx->chain[i_chain];
selector = gx_xchain_calc_selector( xchain, request );
FT_TRACE2(( "Morx Chain No.%d, Address 0x%p\n", i_chain, xchain ));
for ( k_subtbl = 0; k_subtbl < xchain->header.nSubtables; k_subtbl++ )
{
xchain_Subtbl = &xchain->chain_Subtbl[k_subtbl];
if ( !(xchain_Subtbl->header.subFeatureFlags & selector) )
continue;
coverage = xchain_Subtbl->header.coverage;
if ( ( !(coverage & GX_MORX_COVERAGE_ORIENTATION_INDEPENDENT) )
/* Orientation dependent */
&& ((( coverage & GX_MORX_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
/* Vertical only */
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_VERTICAL)
/* But the request is NOT vertical */)
|| (!( coverage & GX_MORX_COVERAGE_HORIZONTAL_OR_VERTICAL_TEXT )
/* Horizontal only */
&& (FTL_Get_FeaturesRequest_Direction(&request->root) != FTL_HORIZONTAL)
/* But the request is NOT horizontal */)))
continue;
order = (coverage & GX_MORX_COVERAGE_ORDER_OF_PROCESSING_GLYPH_ARRAY)
? GXL_DESCENDING
: GXL_ASCENDING;
if ( order == GXL_DESCENDING )
gx_glyphs_array_reverse( out->glyphs, out->length );
subtable_type = coverage & GX_MORX_COVERAGE_SUBTABLE_TYPE;
FT_TRACE2(( "\tSubtable No.%d, Address 0x%p, Type %d\n",
k_subtbl, xchain_Subtbl, subtable_type ));
switch ( subtable_type )
{
case GX_MORX_REARRANGEMENT_SUBTABLE:
error = gx_xrearrangement_subst( xchain_Subtbl->body.rearrangement,
request->initial_state,
out );
break;
case GX_MORX_CONTEXTUAL_SUBTABLE:
error = gx_xcontextual_subst( xchain_Subtbl->body.contextual,
request->initial_state,
out );
break;
case GX_MORX_LIGATURE_SUBTABLE:
error = gx_xligature_subst( xchain_Subtbl->body.ligature,
request->initial_state,
out );
break;
case GX_MORX_RESERVED_SUBTABLE:
FT_ERROR(("Reserved format\n"));
break;
case GX_MORX_NONCONTEXTUAL_SUBTABLE:
error = gx_xnoncontextual_subst( xchain_Subtbl->body.noncontextual, out );
break;
case GX_MORX_INSERTION_SUBTABLE:
error = gx_xinsertion_subst ( xchain_Subtbl->body.insertion,
request->initial_state,
out );
break;
}
if ( order == GXL_DESCENDING )
gx_glyphs_array_reverse( out->glyphs, out->length );
}
}
return FT_Err_Ok;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** Kerning ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
#undef PAIR_TAG
#define PAIR_TAG( left, right ) ( ( (FT_ULong)left << 16 ) | \
(FT_ULong)right )
static FT_Pos gx_kern_get_fmt0( GX_KerningSubtableFormat0Body fmt0,
FT_UShort left_glyph,
FT_UShort right_glyph );
static int gx_kern_fmt0_compar ( const void * a, const void * b);
static FT_Pos gx_kern_get_fmt1( GX_KerningSubtableFormat1Body fmt1,
FT_UShort left_glyph,
FT_UShort right_glyph );
static FT_Pos gx_kern_get_fmt2( GX_KerningSubtableFormat2Body fmt2,
FT_UShort left_glyph,
FT_UShort right_glyph );
static FT_Pos gx_kern_get_fmt3( GX_KerningSubtableFormat3Body fmt3,
FT_UShort left_glyph,
FT_UShort right_glyph );
FT_LOCAL_DEF( FT_Error )
gx_kern_get_pair_kerning ( GX_Kern kern,
FT_UShort left_glyph,
FT_UShort right_glyph,
FTL_Direction dir,
FT_Vector* kerning )
{
FT_Error error = GX_Err_Ok;
FT_ULong i;
GX_KerningSubtable subtable;
GX_KerningSubtableHeader header;
FT_UShort coverage;
GX_KerningFormat fmt;
GX_KerningSubtableBody body;
FT_Pos * value;
kerning->x = 0;
kerning->y = 0;
for ( i = 0; i < kern->nTables; i++ )
{
subtable = &(kern->subtables[i]);
header = &subtable->header;
coverage = header->coverage;
if ( coverage & GX_KERN_COVERAGE_VERTICAL )
{
if ( dir != FTL_VERTICAL )
continue;
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
value = &kerning->x;
else
value = &kerning->y;
}
else
{
if ( dir != FTL_HORIZONTAL )
continue;
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
value = &kerning->y;
else
value = &kerning->x;
}
fmt = coverage & GX_KERN_COVERAGE_FORMAT_MASK;
body = &subtable->body;
switch ( fmt )
{
case GX_KERN_FMT_ORDERED_LIST_OF_KERNING_PAIRS:
*value += gx_kern_get_fmt0 ( body->fmt0, left_glyph, right_glyph );
break;
case GX_KERN_FMT_STATE_TABLE_FOR_CONTEXTUAL_KERNING:
*value += gx_kern_get_fmt1 ( body->fmt1, left_glyph, right_glyph );
break;
case GX_KERN_FMT_SIMPLE_NXM_ARRAY_OF_KERNING_VALUES:
*value += gx_kern_get_fmt2 ( body->fmt2, left_glyph, right_glyph );
break;
case GX_KERN_FMT_SIMPLE_NXM_ARRAY_OF_KERNING_INDICES:
*value += gx_kern_get_fmt3 ( body->fmt3, left_glyph, right_glyph );
break;
default:
break;
}
}
return error;
}
static FT_Pos
gx_kern_get_fmt0( GX_KerningSubtableFormat0Body fmt0,
FT_UShort left_glyph,
FT_UShort right_glyph )
{
GX_KerningSubtableFormat0Entry entry;
FT_ULong search_tag = PAIR_TAG( left_glyph, right_glyph );
if ( !fmt0->nPairs )
return 0;
entry = ft_bsearch(&search_tag,
fmt0->entries,
fmt0->nPairs,
sizeof(*fmt0->entries),
gx_kern_fmt0_compar);
if ( entry )
return entry->value;
else
return 0;
}
static int
gx_kern_fmt0_compar ( const void * search_tag, const void * entry)
{
FT_ULong tag;
tag = PAIR_TAG( ((GX_KerningSubtableFormat0Entry)entry)->left,
((GX_KerningSubtableFormat0Entry)entry)->right );
if ( *(FT_ULong*)search_tag < tag )
return -1;
else if ( *(FT_ULong*)search_tag > tag )
return 1;
else
return 0;
}
static FT_Pos
gx_kern_get_fmt1( GX_KerningSubtableFormat1Body fmt1,
FT_UShort left_glyph, FT_UShort right_glyph )
{
return 0; /* Do nothing */
}
static FT_Pos
gx_kern_get_fmt2( GX_KerningSubtableFormat2Body fmt2,
FT_UShort left_glyph, FT_UShort right_glyph )
{
GX_KerningSubtableFormat2ClassTable left_class, right_class;
FT_UShort left_index, right_index;
FT_Byte kern_index;
left_class = &fmt2->leftClass;
right_class = &fmt2->rightClass;
if (( left_class->firstGlyph > left_glyph )
|| ( !( left_class->firstGlyph + left_class->nGlyphs > left_glyph ) ))
return 0;
if (( right_class->firstGlyph > right_glyph )
|| ( !( right_class->firstGlyph + right_class->nGlyphs > right_glyph ) ))
return 0;
left_index = left_glyph - left_class->firstGlyph;
right_index = right_glyph - right_class->firstGlyph;
FT_ASSERT( left_index < left_class->max_class );
FT_ASSERT( right_index < right_class->max_class );
kern_index = left_class->classes[left_index]
+ right_class->classes[right_index];
return fmt2->values[kern_index];
}
static FT_Pos
gx_kern_get_fmt3( GX_KerningSubtableFormat3Body fmt3,
FT_UShort left_glyph, FT_UShort right_glyph )
{
FT_Byte left_class, right_class;
FT_Byte kern_index;
if ( !( fmt3->glyphCount > left_glyph ) &&
( fmt3->glyphCount > right_glyph ) )
return 0;
left_class = fmt3->leftClass[left_glyph];
right_class = fmt3->rightClass[right_glyph];
kern_index = fmt3->kernIndex[left_class * fmt3->rightClassCount + right_class];
FT_ASSERT ( kern_index < fmt3->kernValueCount );
return fmt3->kernValue[kern_index];
}
FT_LOCAL_DEF( FT_Error )
gx_kern_get_contextual_kerning( GX_Kern kern,
FTL_GlyphArray garray,
FTL_Direction dir,
GXL_Initial_State initial_state,
FT_Vector * kerning )
{
GX_KerningSubtable subtable;
GX_KerningSubtableHeader header;
FT_UShort coverage;
GX_KerningFormat fmt;
GX_KerningSubtableFormat1Body fmt1;
FT_Bool cross_stream = 0;
FT_ULong i;
for ( i = 0; i < kern->nTables; i++ )
{
subtable = &(kern->subtables[i]);
header = &subtable->header;
coverage = header->coverage;
if ( coverage & GX_KERN_COVERAGE_VERTICAL )
{
if ( dir != FTL_VERTICAL )
continue;
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
cross_stream = 1;
}
else
{
if ( dir != FTL_HORIZONTAL )
continue;
if ( coverage & GX_KERN_COVERAGE_CROSS_STREAM )
cross_stream = 1;
}
fmt = coverage & GX_KERN_COVERAGE_FORMAT_MASK;
if ( fmt != GX_KERN_FMT_STATE_TABLE_FOR_CONTEXTUAL_KERNING )
continue;
fmt1 = subtable->body.fmt1;
gx_contextual_kerning_calc( fmt1,
garray, dir, cross_stream, initial_state,
kerning );
}
return FT_Err_Ok;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** Tracking ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
typedef enum track_interpolation_range_ {
TRACK_NO_INTERPOLATION = 0,
TRACK_INTERPOLATION,
TRACK_EXTRAPOLATION_SMALLER,
TRACK_EXTRAPOLATION_LARGER
} track_interpolation_range;
static track_interpolation_range track_find_entry ( GX_TrackData track_data,
FT_Fixed track,
GX_TrackTableEntry * smaller_entry,
GX_TrackTableEntry * larger_entry );
static track_interpolation_range track_find_size ( GX_TrackData track_data,
FT_Fixed size,
FT_Fixed *smaller_size, FT_UShort *smaller_size_index,
FT_Fixed *larger_size, FT_UShort *larger_size_index );
static FT_FWord track_interpolate( FT_Fixed x_t,
FT_Fixed x_p, FT_Fixed x_q,
FT_FWord y_p, FT_FWord y_q );
FT_LOCAL_DEF( FT_Error )
gx_trak_get( GX_Trak trak, FT_Fixed track, FT_Fixed size, FTL_Direction dir, FT_FWord* value )
{
GX_TrackData track_data = ( dir == FTL_HORIZONTAL )
? (&trak->horizData)
: (&trak->vertData);
GX_TrackTableEntry larger_entry = NULL, smaller_entry;
FT_Fixed larger_track, smaller_track;
track_interpolation_range entry_range;
FT_UShort larger_size_index, smaller_size_index;
FT_Fixed larger_size = 0, smaller_size;
track_interpolation_range size_range;
FT_FWord a, b, c, d;
FT_FWord p, q;
if ( !track_data )
{
*value = 0;
return FT_Err_Ok;
}
entry_range = track_find_entry ( track_data, track, &smaller_entry, &larger_entry );
larger_track = larger_entry->track;
smaller_track = smaller_entry->track;
size_range = track_find_size ( track_data, size,
&smaller_size, &smaller_size_index,
&larger_size, &larger_size_index );
a = smaller_entry->tracking_value[smaller_size_index];
b = smaller_entry->tracking_value[larger_size_index];
c = larger_entry->tracking_value[smaller_size_index];
d = larger_entry->tracking_value[larger_size_index];
/* -------------------------------------------------------------------
^ size
|
B | D
|
A | C
--------------+---------------> track
O|
A = ( smaller_track, smaller_size, a )
B = ( smaller_track, larger_size, b )
C = ( larger_track, smaller_size, c )
D = ( larger_track, larger_size, d )
-----------V---------------------------
P = ( track, smaller_size, p )
Q = ( track larger_size, q )
--------------------------V------------
T = ( track, size, *value)
------------------------------------------------------------------- */
if ( entry_range == TRACK_NO_INTERPOLATION )
{
p = a;
q = b;
}
else
{
p = track_interpolate ( track,
smaller_track, larger_track,
a, c );
q = track_interpolate ( track,
smaller_track, larger_track,
b, d );
}
if ( size_range == TRACK_NO_INTERPOLATION )
*value = p;
else
*value = track_interpolate( size,
smaller_size, larger_size,
p, q );
return FT_Err_Ok;
}
static track_interpolation_range
track_find_entry ( GX_TrackData track_data,
FT_Fixed track,
GX_TrackTableEntry * smaller_entry,
GX_TrackTableEntry * larger_entry )
{
FT_UShort i;
track_interpolation_range range;
FT_Bool extrapolation = FALSE;
if ( track < track_data->trackTable[0].track )
{
i = 0;
range = TRACK_EXTRAPOLATION_SMALLER;
extrapolation = TRUE;
}
else if ( track_data->trackTable[track_data->nTracks - 1].track < track )
{
i = track_data->nTracks - 2;
range = TRACK_EXTRAPOLATION_LARGER;
extrapolation = TRUE;
}
if ( extrapolation )
{
*smaller_entry = &track_data->trackTable[i];
*larger_entry = &track_data->trackTable[i + 1];
return range;
}
for ( i = 0; i < track_data->nTracks; i++ )
{
*smaller_entry = *larger_entry;
*larger_entry = &track_data->trackTable[i];
if ( track == (*larger_entry)->track )
{
*smaller_entry = *larger_entry;
return TRACK_NO_INTERPOLATION;
}
else if ( track < (*larger_entry)->track )
return TRACK_INTERPOLATION;
}
GX_ASSERT_NOT_REACHED();
return TRACK_INTERPOLATION;
}
static track_interpolation_range
track_find_size ( GX_TrackData track_data,
FT_Fixed size,
FT_Fixed *smaller_size, FT_UShort *smaller_size_index,
FT_Fixed *larger_size, FT_UShort *larger_size_index)
{
FT_UShort i;
track_interpolation_range range;
FT_Bool extrapolation = FALSE;
if ( size < track_data->sizeTable[0] )
{
i = 0;
range = TRACK_EXTRAPOLATION_SMALLER;
extrapolation = TRUE;
}
else if ( track_data->sizeTable[track_data->nSizes - 1] < size )
{
i = track_data->nSizes - 2;
range = TRACK_EXTRAPOLATION_LARGER;
extrapolation = TRUE;
}
if ( extrapolation )
{
*smaller_size = track_data->sizeTable[i];
*smaller_size_index = i;
*larger_size = track_data->sizeTable[i + 1];
*larger_size_index = i + 1;
return range;
}
for ( i = 0; i < track_data->nSizes; i++ )
{
*smaller_size = *larger_size;
*smaller_size_index = *larger_size_index;
*larger_size = track_data->sizeTable[i];
*larger_size_index = i;
if ( size == *larger_size )
{
*smaller_size = *larger_size;
*smaller_size_index = *larger_size_index;
return TRACK_NO_INTERPOLATION;
}
else if ( size < *larger_size )
return TRACK_INTERPOLATION;
}
GX_ASSERT_NOT_REACHED();
return TRACK_INTERPOLATION;
}
static FT_FWord
track_interpolate( FT_Fixed x_t,
FT_Fixed x_p, FT_Fixed x_q,
FT_FWord y_p, FT_FWord y_q )
{
FT_FWord y_t;
/* TODO: better to use FT_MulDiv_No_Round. */
y_t = ( y_q * ( x_t - x_p ) + y_p * ( x_q - x_t )) / (x_q - x_p);
return y_t;
}
FT_LOCAL_DEF( FT_UShort )
gx_trak_count_name_index( GX_Trak trak )
{
GX_TrackData track_data;
GX_TrackTableEntry track_table;
FT_UShort nTracks;
FT_UShort i;
FT_UShort h = 0;
FT_UShort v = 0;
/*
* Horiz
*/
track_data = &trak->horizData;
nTracks = track_data->nTracks;
for ( i = 0; i < nTracks; i++ )
{
track_table = &track_data->trackTable[i];
if ( track_table->nameIndex )
h++;
}
/*
* Vert
*/
track_data = &trak->vertData;
nTracks = track_data->nTracks;
for ( i = 0; i < nTracks; i++ )
{
track_table = &track_data->trackTable[i];
if ( track_table->nameIndex )
h++;
}
return h + v;
}
FT_LOCAL( FT_Error )
gx_trak_get_name ( GX_Trak trak,
FT_UShort index,
FT_UShort * name_index,
FTL_Direction * dir,
FT_Fixed * track )
{
GX_TrackData track_data;
GX_TrackTableEntry track_table;
FT_UShort i;
/*
* Horiz
*/
track_data = &trak->horizData;
for ( i = 0; i < track_data->nTracks; i++ )
{
track_table = &track_data->trackTable[i];
if ( track_table->nameIndex == 0 )
continue ;
if ( index == 0 )
goto Set_Values;
else
index--;
}
/*
* Vert
*/
track_data = &trak->vertData;
for ( i = 0; i < track_data->nTracks; i++ )
{
track_table = &track_data->trackTable[i];
if ( track_table->nameIndex == 0 )
continue ;
if ( index == 0 )
goto Set_Values;
else
index--;
}
return GX_Err_Invalid_Argument;
Set_Values:
*name_index = track_table->nameIndex;
*dir = ( track_data = &trak->horizData )
? FTL_HORIZONTAL
: FTL_VERTICAL;
*track = track_table->track;
return FT_Err_Ok;
}