/***************************************************************************/ /* */ /* 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 #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; }