ref: d0e6ad636f6758eb0e06e88088d2970737df6a59
dir: /src/pshinter/pshrec.c/
#include <ft2build.h> #include FT_FREETYPE_H #include FT_INTERNAL_OBJECTS_H #include FT_INTERNAL_DEBUG_H #include "pshrec.h" #include "pshalgo.h" #ifdef DEBUG_HINTER extern PS_Hints ps_debug_hints = 0; extern int ps_debug_no_horz_hints = 0; extern int ps_debug_no_vert_hints = 0; #endif /***********************************************************************/ /***********************************************************************/ /***** *****/ /***** PS_HINT MANAGEMENT *****/ /***** *****/ /***********************************************************************/ /***********************************************************************/ /* destroy hints table */ static void ps_hint_table_done( PS_Hint_Table table, FT_Memory memory ) { FREE( table->hints ); table->num_hints = 0; table->max_hints = 0; } /* ensure that a table can contain "count" elements */ static FT_Error ps_hint_table_ensure( PS_Hint_Table table, FT_UInt count, FT_Memory memory ) { FT_UInt old_max = table->max_hints; FT_UInt new_max = count; FT_Error error = 0; if ( new_max > old_max ) { /* try to grow the table */ new_max = ( new_max + 7 ) & -8; if ( !REALLOC_ARRAY( table->hints, old_max, new_max, PS_HintRec ) ) table->max_hints = new_max; } return error; } static FT_Error ps_hint_table_alloc( PS_Hint_Table table, FT_Memory memory, PS_Hint *ahint ) { FT_Error error = 0; FT_UInt count; PS_Hint hint = 0; count = table->num_hints; count++; if ( count >= table->max_hints ) { error = ps_hint_table_ensure( table, count, memory ); if (error) goto Exit; } hint = table->hints + count-1; hint->pos = 0; hint->len = 0; hint->flags = 0; table->num_hints = count; Exit: *ahint = hint; return error; } /***********************************************************************/ /***********************************************************************/ /***** *****/ /***** PS_MASK MANAGEMENT *****/ /***** *****/ /***********************************************************************/ /***********************************************************************/ /* destroy mask */ static void ps_mask_done( PS_Mask mask, FT_Memory memory ) { FREE( mask->bytes ); mask->num_bits = 0; mask->max_bits = 0; mask->end_point = 0; } /* ensure that a mask can contain "count" bits */ static FT_Error ps_mask_ensure( PS_Mask mask, FT_UInt count, FT_Memory memory ) { FT_UInt old_max = (mask->max_bits + 7) >> 3; FT_UInt new_max = (count + 7) >> 3; FT_Error error = 0; if ( new_max > old_max ) { new_max = ( new_max + 7 ) & -8; if ( !REALLOC_ARRAY( mask->bytes, old_max, new_max, FT_Byte ) ) mask->max_bits = new_max*8; } return error; } /* test a bit value in a given mask */ static FT_Int ps_mask_test_bit( PS_Mask mask, FT_Int index ) { if ( (FT_UInt)index >= mask->num_bits ) return 0; return mask->bytes[index >> 3] & (0x80 >> (index & 7)); } /* clear a given bit */ static void ps_mask_clear_bit( PS_Mask mask, FT_Int index ) { FT_Byte* p; if ( (FT_UInt)index >= mask->num_bits ) return; p = mask->bytes + (index >> 3); p[0] = (FT_Byte)( p[0] & ~(0x80 >> (index & 7)) ); } /* set a given bit, eventually grow the mask */ static FT_Error ps_mask_set_bit( PS_Mask mask, FT_Int index, FT_Memory memory ) { FT_Error error = 0; FT_Byte* p; if ( index < 0 ) goto Exit; if ( (FT_UInt)index >= mask->num_bits ) { error = ps_mask_ensure( mask, index+1, memory ); if (error) goto Exit; mask->num_bits = index+1; } p = mask->bytes + (index >> 3); p[0] = (FT_Byte)( p[0] | (0x80 >> (index & 7)) ); Exit: return error; } /* destroy mask table */ static void ps_mask_table_done( PS_Mask_Table table, FT_Memory memory ) { FT_UInt count = table->max_masks; PS_Mask mask = table->masks; for ( ; count > 0; count--, mask++ ) ps_mask_done( mask, memory ); FREE( table->masks ); table->num_masks = 0; table->max_masks = 0; } /* ensure that a mask table can contain "count" masks */ static FT_Error ps_mask_table_ensure( PS_Mask_Table table, FT_UInt count, FT_Memory memory ) { FT_UInt old_max = table->max_masks; FT_UInt new_max = count; FT_Error error = 0; if ( new_max > old_max ) { new_max = (new_max+7) & -8; if ( !REALLOC_ARRAY( table->masks, old_max, new_max, PS_MaskRec ) ) table->max_masks = new_max; } return error; } /* allocate a new mask in a table */ static FT_Error ps_mask_table_alloc( PS_Mask_Table table, FT_Memory memory, PS_Mask *amask ) { FT_UInt count; FT_Error error = 0; PS_Mask mask = 0; count = table->num_masks; count++; if ( count > table->max_masks ) { error = ps_mask_table_ensure( table, count, memory ); if (error) goto Exit; } mask = table->masks + count - 1; mask->num_bits = 0; mask->end_point = 0; table->num_masks = count; Exit: *amask = mask; return error; } /* return last hint mask in a table, create one if the table is empty */ static FT_Error ps_mask_table_last( PS_Mask_Table table, FT_Memory memory, PS_Mask *amask ) { FT_Error error = 0; FT_UInt count; PS_Mask mask; count = table->num_masks; if ( count == 0 ) { error = ps_mask_table_alloc( table, memory, &mask ); if (error) goto Exit; } else mask = table->masks + count-1; Exit: *amask = mask; return error; } /* set a new mask to a given bit range */ static FT_Error ps_mask_table_set_bits( PS_Mask_Table table, FT_Byte* source, FT_UInt bit_pos, FT_UInt bit_count, FT_Memory memory ) { FT_Error error = 0; PS_Mask mask; /* allocate new mask, and grow it to "bit_count" bits */ error = ps_mask_table_alloc( table, memory, &mask ); if (error) goto Exit; error = ps_mask_ensure( mask, bit_count, memory ); if (error) goto Exit; mask->num_bits = bit_count; /* now, copy bits */ { FT_Byte* read = source + (bit_pos >> 3); FT_Int rmask = 0x80 >> (bit_pos & 7); FT_Byte* write = mask->bytes; FT_Int wmask = 0x80; FT_Int val; for ( ; bit_count > 0; bit_count-- ) { val = write[0] & ~wmask; if ( read[0] & rmask ) val |= wmask; write[0] = (FT_Byte) val; rmask >>= 1; if ( rmask == 0 ) { read++; rmask = 0x80; } wmask >>= 1; if ( wmask == 0 ) { write++; wmask = 0x80; } } } Exit: return error; } /* test wether two masks in a table intersect */ static FT_Int ps_mask_table_test_intersect( PS_Mask_Table table, FT_Int index1, FT_Int index2 ) { PS_Mask mask1 = table->masks + index1; PS_Mask mask2 = table->masks + index2; FT_Byte* p1 = mask1->bytes; FT_Byte* p2 = mask2->bytes; FT_UInt count1 = mask1->num_bits; FT_UInt count2 = mask2->num_bits; FT_UInt count; count = ( count1 <= count2 ) ? count1 : count2; for ( ; count >= 8; count -= 8 ) { if ( p1[0] & p2[0] ) return 1; p1++; p2++; } if ( count == 0 ) return 0; return ( p1[0] & p2[0] ) & ~(0xFF >> count); } /* merge two masks, used by ps_mask_table_merge_all */ static FT_Error ps_mask_table_merge( PS_Mask_Table table, FT_Int index1, FT_Int index2, FT_Memory memory ) { FT_UInt temp; FT_Error error = 0; /* swap index1 and index2 so that index1 < index2 */ if ( index1 > index2 ) { temp = index1; index1 = index2; index2 = index1; } if ( index1 < index2 && index1 >= 0 && index2 < (FT_Int)table->num_masks ) { /* we need to merge the bitsets of index1 and index2 with a */ /* simple union.. */ PS_Mask mask1 = table->masks + index1; PS_Mask mask2 = table->masks + index2; FT_UInt count1 = mask1->num_bits; FT_UInt count2 = mask2->num_bits; FT_Int delta; if ( count2 > 0 ) { FT_UInt pos; FT_Byte* read; FT_Byte* write; /* if "count2" is greater than "count1", we need to grow the */ /* first bitset, and clear the highest bits.. */ if ( count2 > count1 ) { error = ps_mask_ensure( mask1, count2, memory ); if (error) goto Exit; for ( pos = count1; pos < count2; pos++ ) ps_mask_clear_bit( mask1, pos ); } /* merge (union) the bitsets */ read = mask2->bytes; write = mask1->bytes; pos = (FT_UInt)((count2+7) >> 3); for ( ; pos > 0; pos-- ) { write[0] = (FT_Byte)( write[0] | read[0] ); write++; read++; } } /* now, remove "mask2" from the list, we need to keep the masks */ /* sorted in order of importance, so move table elements.. */ mask2->num_bits = 0; mask2->end_point = 0; delta = table->num_masks-1 - index2; /* number of masks to move */ if ( delta > 0 ) { /* move to end of table for reuse */ PS_MaskRec dummy = *mask2; memmove( mask2, mask2+1, delta*sizeof(PS_MaskRec) ); mask2[delta] = dummy; } table->num_masks--; } else FT_ERROR(( "%s: ignoring invalid indices (%d,%d)\n", index1, index2 )); Exit: return error; } /* try to merge all masks in a given table, this is used to merge */ /* all counter masks into independent counter "paths" */ /* */ static FT_Error ps_mask_table_merge_all( PS_Mask_Table table, FT_Memory memory ) { FT_Int index1, index2; FT_Error error = 0; for ( index1 = table->num_masks-1; index1 > 0; index1-- ) { for ( index2 = index1-1; index2 >= 0; index2-- ) { if ( ps_mask_table_test_intersect( table, index1, index2 ) ) { error = ps_mask_table_merge( table, index2, index1, memory ); if (error) goto Exit; break; } } } Exit: return error; } /***********************************************************************/ /***********************************************************************/ /***** *****/ /***** PS_DIMENSION MANAGEMENT *****/ /***** *****/ /***********************************************************************/ /***********************************************************************/ /* finalize a given dimension */ static void ps_dimension_done( PS_Dimension dimension, FT_Memory memory ) { ps_mask_table_done( &dimension->counters, memory ); ps_mask_table_done( &dimension->masks, memory ); ps_hint_table_done( &dimension->hints, memory ); } /* initialise a given dimension */ static void ps_dimension_init( PS_Dimension dimension ) { dimension->hints.num_hints = 0; dimension->masks.num_masks = 0; dimension->counters.num_masks = 0; } #if 0 /* set a bit at a given index in the current hint mask */ static FT_Error ps_dimension_set_mask_bit( PS_Dimension dim, FT_UInt index, FT_Memory memory ) { PS_Mask mask; FT_Error error = 0; /* get last hint mask */ error = ps_mask_table_last( &dim->masks, memory, &mask ); if (error) goto Exit; error = ps_mask_set_bit( mask, index, memory ); Exit: return error; } #endif /* set the end point in a mask, called from "End" & "Reset" methods */ static void ps_dimension_end_mask( PS_Dimension dim, FT_UInt end_point ) { FT_UInt count = dim->masks.num_masks; PS_Mask mask; if ( count > 0 ) { mask = dim->masks.masks + count-1; mask->end_point = end_point; } } /* set the end point in the current mask, then create a new empty one */ /* (called by "Reset" method) */ static FT_Error ps_dimension_reset_mask( PS_Dimension dim, FT_UInt end_point, FT_Memory memory ) { PS_Mask mask; /* end current mask */ ps_dimension_end_mask( dim, end_point ); /* allocate new one */ return ps_mask_table_alloc( &dim->masks, memory, &mask ); } /* set a new mask, called from the "T2Stem" method */ static FT_Error ps_dimension_set_mask_bits( PS_Dimension dim, const FT_Byte* source, FT_UInt source_pos, FT_UInt source_bits, FT_UInt end_point, FT_Memory memory ) { FT_Error error = 0; /* reset current mask, if any */ error = ps_dimension_reset_mask( dim, end_point, memory ); if (error) goto Exit; /* set bits in new mask */ error = ps_mask_table_set_bits( &dim->masks, (FT_Byte*)source, source_pos, source_bits, memory ); Exit: return error; } /* add a new single stem (called from "T1Stem" method) */ static FT_Error ps_dimension_add_t1stem( PS_Dimension dim, FT_Int pos, FT_Int len, FT_Memory memory, FT_Int *aindex ) { FT_Error error = 0; FT_UInt flags = 0; /* detect ghost stem */ if ( len < 0 ) { flags |= PS_HINT_FLAG_GHOST; if ( len == -21 ) { flags |= PS_HINT_FLAG_BOTTOM; pos += len; } len = 0; } if (aindex) *aindex = -1; /* now, lookup stem in the current hints table */ { PS_Mask mask; FT_UInt index; FT_UInt max = dim->hints.num_hints; PS_Hint hint = dim->hints.hints; for ( index = 0; index < max; index++, hint++ ) { if ( hint->pos == pos && hint->len == len ) break; } /* we need to create a new hint in the table */ if ( index >= max ) { error = ps_hint_table_alloc( &dim->hints, memory, &hint ); if (error) goto Exit; hint->pos = pos; hint->len = len; hint->flags = flags; } /* now, store the hint in the current mask */ error = ps_mask_table_last( &dim->masks, memory, &mask ); if (error) goto Exit; error = ps_mask_set_bit( mask, index, memory ); if (error) goto Exit; if ( aindex ) *aindex = (FT_Int)index; } Exit: return error; } /* add a "hstem3/vstem3" counter to our dimension table */ static FT_Error ps_dimension_add_counter( PS_Dimension dim, FT_Int hint1, FT_Int hint2, FT_Int hint3, FT_Memory memory ) { FT_Error error = 0; FT_UInt count = dim->counters.num_masks; PS_Mask counter = dim->counters.masks; /* try to find an existing counter mask that already uses */ /* one of these stems here.. */ for ( ; count > 0; count--, counter++ ) { if ( ps_mask_test_bit( counter, hint1 ) || ps_mask_test_bit( counter, hint2 ) || ps_mask_test_bit( counter, hint3 ) ) break; } /* creat a new counter when needed */ if ( count == 0 ) { error = ps_mask_table_alloc( &dim->counters, memory, &counter ); if (error) goto Exit; } /* now, set the bits for our hints in the counter mask */ error = ps_mask_set_bit( counter, hint1, memory ); if (error) goto Exit; error = ps_mask_set_bit( counter, hint2, memory ); if (error) goto Exit; error = ps_mask_set_bit( counter, hint3, memory ); if (error) goto Exit; Exit: return error; } /* end of recording session for a given dimension */ static FT_Error ps_dimension_end( PS_Dimension dim, FT_UInt end_point, FT_Memory memory ) { /* end hint mask table */ ps_dimension_end_mask( dim, end_point ); /* merge all counter masks into independent "paths" */ return ps_mask_table_merge_all( &dim->counters, memory ); } /***********************************************************************/ /***********************************************************************/ /***** *****/ /***** PS_RECORDER MANAGEMENT *****/ /***** *****/ /***********************************************************************/ /***********************************************************************/ /* destroy hints */ FT_LOCAL void ps_hints_done( PS_Hints hints ) { FT_Memory memory = hints->memory; ps_dimension_done( &hints->dimension[0], memory ); ps_dimension_done( &hints->dimension[1], memory ); hints->error = 0; hints->memory = 0; } FT_LOCAL FT_Error ps_hints_init( PS_Hints hints, FT_Memory memory ) { memset( hints, 0, sizeof(*hints) ); hints->memory = memory; return 0; } /* initialise a hints for a new session */ static void ps_hints_open( PS_Hints hints, PS_Hint_Type hint_type ) { switch (hint_type) { case PS_HINT_TYPE_1: case PS_HINT_TYPE_2: { hints->error = 0; hints->hint_type = hint_type; ps_dimension_init( &hints->dimension[0] ); ps_dimension_init( &hints->dimension[1] ); } break; default: hints->error = FT_Err_Invalid_Argument; hints->hint_type = hint_type; FT_ERROR(( "%s.init: invalid charstring type !!\n", "t1fitter.hints" )); } } /* add one or more stems to the current hints table */ static void ps_hints_stem( PS_Hints hints, FT_Int dimension, FT_UInt count, FT_Long* stems ) { if ( !hints->error ) { /* limit "dimension" to 0..1 */ if ( dimension < 0 || dimension > 1 ) { FT_ERROR(( "ps.hints.stem: invalid dimension (%d) used\n", dimension )); dimension = (dimension != 0); } /* record the stems in the current hints/masks table */ switch ( hints->hint_type ) { case PS_HINT_TYPE_1: /* Type 1 "hstem" or "vstem" operator */ case PS_HINT_TYPE_2: /* Type 2 "hstem" or "vstem" operator */ { PS_Dimension dim = &hints->dimension[dimension]; for ( ; count > 0; count--, stems += 2 ) { FT_Error error; FT_Memory memory = hints->memory; error = ps_dimension_add_t1stem( dim, stems[0], stems[1], memory, NULL ); if (error) { FT_ERROR(( "t1f.hints.stem: could not add stem" " (%d,%d) to hints table\n", stems[0], stems[1] )); hints->error = error; return; }; } } break; default: FT_ERROR(( "t1f.hints.stem: called with invalid hint type (%d)\n", hints->hint_type )); ; } } } /* add one Type1 counter stem to the current hints table */ static void ps_hints_t1stem3( PS_Hints hints, FT_Int dimension, FT_Long* stems ) { FT_Error error = 0; if (!hints->error) { PS_Dimension dim; FT_Memory memory = hints->memory; FT_Int count; FT_Int index[3]; /* limit "dimension" to 0..1 */ if ( dimension < 0 || dimension > 1 ) { FT_ERROR(( "t1f.hints.stem: invalid dimension (%d) used\n", dimension )); dimension = (dimension != 0); } dim = &hints->dimension[dimension]; /* there must be 6 elements in the 'stem' array */ if ( hints->hint_type == PS_HINT_TYPE_1 ) { /* add the three stems to our hints/masks table */ for ( count = 0; count < 3; count++, stems += 2 ) { error = ps_dimension_add_t1stem( dim, stems[0], stems[1], memory, &index[count] ); if (error) goto Fail; } /* now, add the hints to the counters table */ error = ps_dimension_add_counter( dim, index[0], index[1], index[2], memory ); if (error) goto Fail; } else { FT_ERROR(( "t1f.hints.stem3: called with invalid hint type !!\n" )); error = FT_Err_Invalid_Argument; goto Fail; } } return; Fail: FT_ERROR(( "t1f.hints.stem3: could not add counter stems to table\n" )); hints->error = error; } /* reset hints (only with Type 1 hints) */ static void ps_hints_t1reset( PS_Hints hints, FT_UInt end_point ) { FT_Error error = 0; if ( !hints->error ) { FT_Memory memory = hints->memory; if ( hints->hint_type == PS_HINT_TYPE_1 ) { error = ps_dimension_reset_mask( &hints->dimension[0], end_point, memory ); if (error) goto Fail; error = ps_dimension_reset_mask( &hints->dimension[1], end_point, memory ); if (error) goto Fail; } else { /* invalid hint type */ error = FT_Err_Invalid_Argument; goto Fail; } } return; Fail: hints->error = error; } /* Type2 "hintmask" operator, add a new hintmask to each direction */ static void ps_hints_t2mask( PS_Hints hints, FT_UInt end_point, FT_UInt bit_count, const FT_Byte* bytes ) { FT_Error error; if ( !hints->error ) { PS_Dimension dim = hints->dimension; FT_Memory memory = hints->memory; FT_UInt count1 = dim[0].hints.num_hints; FT_UInt count2 = dim[1].hints.num_hints; /* check bit count, must be equal to current total hint count */ if ( bit_count != count1 + count2 ) { error = FT_Err_Invalid_Argument; FT_ERROR(( "%s: called with invalid bitcount %d (instead of %d)\n", bit_count, count1+count2 )); goto Fail; } /* set-up new horizontal and vertical hint mask now */ error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1, end_point, memory ); if (error) goto Fail; error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2, end_point, memory ); if (error) goto Fail; } return; Fail: hints->error = error; } static void ps_hints_t2counter( PS_Hints hints, FT_UInt bit_count, const FT_Byte* bytes ) { FT_Error error; if ( !hints->error ) { PS_Dimension dim = hints->dimension; FT_Memory memory = hints->memory; FT_UInt count1 = dim[0].hints.num_hints; FT_UInt count2 = dim[1].hints.num_hints; /* check bit count, must be equal to current total hint count */ if ( bit_count != count1 + count2 ) { error = FT_Err_Invalid_Argument; FT_ERROR(( "%s: called with invalid bitcount %d (instead of %d)\n", bit_count, count1+count2 )); goto Fail; } /* set-up new horizontal and vertical hint mask now */ error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1, 0, memory ); if (error) goto Fail; error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2, 0, memory ); if (error) goto Fail; } return; Fail: hints->error = error; } /* end recording session */ static FT_Error ps_hints_close( PS_Hints hints, FT_UInt end_point ) { FT_Error error; error = hints->error; if (!error) { FT_Error error; FT_Memory memory = hints->memory; PS_Dimension dim = hints->dimension; error = ps_dimension_end( &dim[0], end_point, memory ); if (!error) { error = ps_dimension_end( &dim[1], end_point, memory ); } } #ifdef DEBUG_HINTER if (!error) ps_debug_hints = hints; #endif return error; } /***********************************************************************/ /***********************************************************************/ /***** *****/ /***** TYPE 1 HINTS RECORDING INTERFACE *****/ /***** *****/ /***********************************************************************/ /***********************************************************************/ static void t1_hints_open( T1_Hints hints ) { ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 ); } static void t1_hints_stem( T1_Hints hints, FT_Int dimension, FT_Long* coords ) { ps_hints_stem( (PS_Hints)hints, dimension, 1, coords ); } FT_LOCAL_DEF void t1_hints_funcs_init( T1_Hints_FuncsRec* funcs ) { memset( (char*)funcs, 0, sizeof(*funcs) ); funcs->open = (T1_Hints_OpenFunc) t1_hints_open; funcs->close = (T1_Hints_CloseFunc) ps_hints_close; funcs->stem = (T1_Hints_SetStemFunc) t1_hints_stem; funcs->stem3 = (T1_Hints_SetStem3Func) ps_hints_t1stem3; funcs->reset = (T1_Hints_ResetFunc) ps_hints_t1reset; funcs->apply = (T1_Hints_ApplyFunc) PS_HINTS_APPLY_FUNC; } /***********************************************************************/ /***********************************************************************/ /***** *****/ /***** TYPE 2 HINTS RECORDING INTERFACE *****/ /***** *****/ /***********************************************************************/ /***********************************************************************/ static void t2_hints_open( T2_Hints hints ) { ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_2 ); } static void t2_hints_stems( T2_Hints hints, FT_Int dimension, FT_Int count, FT_Fixed* coords ) { FT_Long stems[32], n, total = count; while (total > 0) { /* determine number of stems to write */ count = total; if ( count > 32 ) count = 32; /* compute integer stem position in font units */ for ( n = 0; n < count*2; n++ ) stems[n] = (coords[n] + 0x8000) >> 16; /* add them to the current dimension */ ps_hints_stem( (PS_Hints)hints, dimension, count, stems ); total -= (count >> 1); } } FT_LOCAL_DEF void t2_hints_funcs_init( T2_Hints_FuncsRec* funcs ) { memset( funcs, 0, sizeof(*funcs) ); funcs->open = (T2_Hints_OpenFunc) t2_hints_open; funcs->close = (T2_Hints_CloseFunc) ps_hints_close; funcs->stems = (T2_Hints_StemsFunc) t2_hints_stems; funcs->hintmask = (T2_Hints_MaskFunc) ps_hints_t2mask; funcs->counter = (T2_Hints_CounterFunc) ps_hints_t2counter; funcs->apply = (T2_Hints_ApplyFunc) PS_HINTS_APPLY_FUNC; }