diff --git a/Jamfile b/Jamfile index fa774b3b9..8fc419bc0 100644 --- a/Jamfile +++ b/Jamfile @@ -15,6 +15,13 @@ FT2_SRC = [ FT2_SubDir src ] ; FT2_LIB = $(LIBPREFIX)freetype ; +if $(DEBUG_HINTER) +{ + CCFLAGS += -DDEBUG_HINTER ; +} + + + # We need "freetype2/include" in the current include path in order to # compile any part of FreeType 2. # @@ -38,8 +45,11 @@ HDRMACRO [ FT2_SubDir include freetype internal internal.h ] ; SubInclude FT2_TOP src ; -# tests files +# tests files (hinter debugging) # -SubInclude FT2_TOP tests ; +if $(DEBUG_HINTER) +{ + SubInclude FT2_TOP tests ; +} # end of top Jamfile diff --git a/src/psaux/t1decode.c b/src/psaux/t1decode.c index d3af4163c..2ceaccc6b 100644 --- a/src/psaux/t1decode.c +++ b/src/psaux/t1decode.c @@ -141,7 +141,7 @@ FT_String* name = (FT_String*)decoder->glyph_names[n]; - if ( name && strcmp( name,glyph_name ) == 0 ) + if ( name && name[0] == glyph_name[0] && strcmp( name,glyph_name ) == 0 ) return n; } @@ -245,6 +245,7 @@ glyph->format = ft_glyph_format_composite; loader->current.num_subglyphs = 2; + goto Exit; } /* First load `bchar' in builder */ @@ -318,7 +319,7 @@ FT_Byte* ip; FT_Byte* limit; T1_Builder* builder = &decoder->builder; - FT_Pos x, y; + FT_Pos x, y, orig_x, orig_y; T1_Hints_Funcs hinter; @@ -345,8 +346,8 @@ error = PSaux_Err_Ok; - x = builder->pos_x; - y = builder->pos_y; + x = orig_x = builder->pos_x; + y = orig_y = builder->pos_y; /* begin hints recording session, if any */ if ( hinter ) @@ -740,8 +741,8 @@ builder->advance.x = top[1]; builder->advance.y = 0; - builder->last.x = x = builder->pos_x + top[0]; - builder->last.y = y = builder->pos_y; + orig_x = builder->last.x = x = builder->pos_x + top[0]; + orig_y = builder->last.y = y = builder->pos_y; /* the `metrics_only' indicates that we only want to compute */ /* the glyph's metrics (lsb + advance width), not load the */ @@ -1016,7 +1017,7 @@ /* record vertical hint */ if ( hinter ) { - top[0] += builder->left_bearing.x; + top[0] += orig_x; hinter->stem( hinter->hints, 1, top ); } @@ -1027,8 +1028,14 @@ /* record vertical counter-controlled hints */ if ( hinter ) + { + FT_Pos dx = orig_x; + + top[0] += dx; + top[2] += dx; + top[4] += dx; hinter->stem3( hinter->hints, 1, top ); - + } break; case op_setcurrentpoint: diff --git a/src/pshinter/Jamfile b/src/pshinter/Jamfile index 081ded470..3b749f171 100644 --- a/src/pshinter/Jamfile +++ b/src/pshinter/Jamfile @@ -10,7 +10,7 @@ SubDirHdrs [ FT2_SubDir src pshinter ] ; if $(FT2_MULTI) { - _sources = pshrec pshglob pshfit pshmod pshoptim ; + _sources = pshrec pshglob pshalgo1 pshalgo2 pshmod ; } else { diff --git a/src/pshinter/pshalgo.h b/src/pshinter/pshalgo.h new file mode 100644 index 000000000..641931124 --- /dev/null +++ b/src/pshinter/pshalgo.h @@ -0,0 +1,19 @@ +#ifndef __PS_HINTER_ALGO_H__ +#define __PS_HINTER_ALGO_H__ + +FT_BEGIN_HEADER + +/* define to choose hinting algorithm */ +#define PSH_ALGORITHM_2 + +#ifdef PSH_ALGORITHM_1 +# include "pshalgo1.h" +# define PS_HINTS_APPLY_FUNC ps1_hints_apply +#else +# include "pshalgo2.h" +# define PS_HINTS_APPLY_FUNC ps2_hints_apply +#endif + +FT_END_HEADER + +#endif /* __PS_HINTER_ALGO_H__ */ diff --git a/src/pshinter/pshalgo1.c b/src/pshinter/pshalgo1.c new file mode 100644 index 000000000..eaa82de3d --- /dev/null +++ b/src/pshinter/pshalgo1.c @@ -0,0 +1,736 @@ +#include +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include "pshalgo1.h" + +#ifdef DEBUG_HINTER + extern PSH1_Hint_Table ps1_debug_hint_table = 0; + extern PSH1_HintFunc ps1_debug_hint_func = 0; +#endif + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** BASIC HINTS RECORDINGS *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + /* return true iff two stem hints overlap */ + static FT_Int + psh1_hint_overlap( PSH1_Hint hint1, + PSH1_Hint hint2 ) + { + return ( hint1->org_pos + hint1->org_len >= hint2->org_pos && + hint2->org_pos + hint2->org_len >= hint1->org_pos ); + } + + + /* destroy hints table */ + static void + psh1_hint_table_done( PSH1_Hint_Table table, + FT_Memory memory ) + { + FREE( table->zones ); + table->num_zones = 0; + table->zone = 0; + + FREE( table->sort ); + FREE( table->hints ); + table->num_hints = 0; + table->max_hints = 0; + table->sort_global = 0; + } + + + /* deactivate all hints in a table */ + static void + psh1_hint_table_deactivate( PSH1_Hint_Table table ) + { + FT_UInt count = table->max_hints; + PSH1_Hint hint = table->hints; + + for ( ; count > 0; count--, hint++ ) + { + psh1_hint_deactivate(hint); + hint->order = -1; + } + } + + + /* internal function used to record a new hint */ + static void + psh1_hint_table_record( PSH1_Hint_Table table, + FT_UInt index ) + { + PSH1_Hint hint = table->hints + index; + + if ( index >= table->max_hints ) + { + FT_ERROR(( "%s.activate: invalid hint index %d\n", index )); + return; + } + + /* ignore active hints */ + if ( psh1_hint_is_active(hint) ) + return; + + psh1_hint_activate(hint); + + /* now scan the current active hint set in order to determine */ + /* if we're overlapping with another segment.. */ + { + PSH1_Hint* sorted = table->sort_global; + FT_UInt count = table->num_hints; + PSH1_Hint hint2; + + hint->parent = 0; + for ( ; count > 0; count--, sorted++ ) + { + hint2 = sorted[0]; + + if ( psh1_hint_overlap( hint, hint2 ) ) + { + hint->parent = hint2; + break; + } + } + } + + if ( table->num_hints < table->max_hints ) + table->sort_global[ table->num_hints++ ] = hint; + else + { + FT_ERROR(( "%s.activate: too many sorted hints !! BUG !!\n", + "ps.fitter" )); + } + } + + + static void + psh1_hint_table_record_mask( PSH1_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit; + + limit = hint_mask->num_bits; + + if ( limit != table->max_hints ) + { + FT_ERROR(( "%s.activate_mask: invalid bit count (%d instead of %d)\n", + "ps.fitter", hint_mask->num_bits, table->max_hints )); + } + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + psh1_hint_table_record( table, index ); + + mask >>= 1; + } + } + + + /* create hints table */ + static FT_Error + psh1_hint_table_init( PSH1_Hint_Table table, + PS_Hint_Table hints, + PS_Mask_Table hint_masks, + PS_Mask_Table counter_masks, + FT_Memory memory ) + { + FT_UInt count = hints->num_hints; + FT_Error error; + + FT_UNUSED(counter_masks); + + /* allocate our tables */ + if ( ALLOC_ARRAY( table->sort, 2*count, PSH1_Hint ) || + ALLOC_ARRAY( table->hints, count, PSH1_HintRec ) || + ALLOC_ARRAY( table->zones, 2*count+1, PSH1_ZoneRec ) ) + goto Exit; + + table->max_hints = count; + table->sort_global = table->sort + count; + table->num_hints = 0; + table->num_zones = 0; + table->zone = 0; + + /* now, initialise the "hints" array */ + { + PSH1_Hint write = table->hints; + PS_Hint read = hints->hints; + + for ( ; count > 0; count--, write++, read++ ) + { + write->org_pos = read->pos; + write->org_len = read->len; + write->flags = read->flags; + } + } + + /* we now need to determine the initial "parent" stems, first */ + /* activate the hints that are given by the initial hint masks */ + if ( hint_masks ) + { + FT_UInt count = hint_masks->num_masks; + PS_Mask mask = hint_masks->masks; + + table->hint_masks = hint_masks; + + for ( ; count > 0; count--, mask++ ) + psh1_hint_table_record_mask( table, mask ); + } + + /* now, do a linear parse in case some hints were left alone */ + if ( table->num_hints != table->max_hints ) + { + FT_UInt index, count; + + FT_ERROR(( "%s.init: missing/incorrect hint masks !!\n" )); + count = table->max_hints; + for ( index = 0; index < count; index++ ) + psh1_hint_table_record( table, index ); + } + + Exit: + return error; + } + + + + static void + psh1_hint_table_activate_mask( PSH1_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit, count; + + limit = hint_mask->num_bits; + count = 0; + + psh1_hint_table_deactivate( table ); + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + { + PSH1_Hint hint = &table->hints[index]; + + if ( !psh1_hint_is_active(hint) ) + { + PSH1_Hint* sort = table->sort; + FT_UInt count2; + PSH1_Hint hint2; + + for ( count2 = count; count2 > 0; count2--, sort++ ) + { + hint2 = sort[0]; + if ( psh1_hint_overlap( hint, hint2 ) ) + { + FT_ERROR(( "%s.activate_mask: found overlapping hints\n", + "psf.hint" )); + break; + } + } + + if ( count2 == 0 ) + { + psh1_hint_activate( hint ); + if ( count < table->max_hints ) + table->sort[count++] = hint; + else + { + FT_ERROR(( "%s.activate_mask: too many active hints\n", + "psf.hint" )); + } + } + } + } + + mask >>= 1; + } + table->num_hints = count; + + /* now, sort the hints, they're guaranteed to not overlap */ + /* so we can compare their "org_pos" field directly.. */ + { + FT_Int i1, i2; + PSH1_Hint hint1, hint2; + PSH1_Hint* sort = table->sort; + + /* a simple bubble sort will do, since in 99% of cases, the hints */ + /* will be already sorted.. and the sort will be linear */ + for ( i1 = 1; i1 < (FT_Int)count; i1++ ) + { + hint1 = sort[i1]; + for ( i2 = i1-1; i2 >= 0; i2-- ) + { + hint2 = sort[i2]; + if ( hint2->org_pos < hint1->org_pos ) + break; + + sort[i1] = hint2; + sort[i2] = hint1; + } + } + } + } + + + + + + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HINTS GRID-FITTING AND OPTIMISATION *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#ifdef DEBUG_HINTER + void + ps_simple_scale( PSH1_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta, + FT_Bool vertical ) + { + PSH1_Hint hint; + FT_UInt count; + + for ( count = 0; count < table->num_hints; count++ ) + { + hint = table->sort[count]; + if ( psh1_hint_is_active(hint) ) + { + hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; + hint->cur_len = FT_MulFix( hint->org_len, scale ); + + if (ps1_debug_hint_func) + ps1_debug_hint_func( hint, vertical ); + } + } + } +#endif + + FT_LOCAL_DEF FT_Error + psh1_hint_table_optimize( PSH1_Hint_Table table, + PSH_Globals globals, + FT_Outline* outline, + FT_Bool vertical ) + { + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + +#ifdef DEBUG_HINTER + if ( ps_debug_no_vert_hints && vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return 0; + } + + if ( ps_debug_no_horz_hints && !vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return 0; + } +#endif + + /* XXXX: for now, we only scale the hints to test all other aspects */ + /* of the Postscript Hinter.. */ + { + PSH1_Hint hint; + FT_UInt count; + + for ( count = 0; count < table->num_hints; count++ ) + { + hint = table->sort[count]; + if ( psh1_hint_is_active(hint) ) + { +# if 1 + FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; + FT_Pos len = FT_MulFix( hint->org_len, scale ); + + FT_Pos fit_center; + FT_Pos fit_len; + + PSH_AlignmentRec align; + + /* compute fitted width/height */ + fit_len = psh_dimension_snap_width( dim, hint->org_len ); + if ( fit_len < 64 ) + fit_len = 64; + else + fit_len = (fit_len + 32 ) & -64; + + hint->cur_len = fit_len; + + /* check blue zones for horizontal stems */ + align.align = 0; + if (!vertical) + { + psh_blues_snap_stem( &globals->blues, + hint->org_pos + hint->org_len, + hint->org_pos, + &align ); + } + + switch (align.align) + { + case PSH_BLUE_ALIGN_TOP: + { + /* the top of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_top - fit_len; + break; + } + + case PSH_BLUE_ALIGN_BOT: + { + /* the bottom of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_bot; + break; + } + + case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: + { + /* both edges of the stem are aligned against blue zones */ + hint->cur_pos = align.align_bot; + hint->cur_len = align.align_top - align.align_bot; + } + break; + + default: + /* normal processing */ + if ( (fit_len/64) & 1 ) + { + /* odd number of pixels */ + fit_center = ((pos + (len >> 1)) & -64) + 32; + } + else + { + /* even number of pixels */ + fit_center = (pos + (len >> 1) + 32) & -64; + } + + hint->cur_pos = fit_center - (fit_len >> 1); + } +# else + hint->cur_pos = (FT_MulFix( hint->org_pos, scale ) + delta + 32) & -64; + hint->cur_len = FT_MulFix( hint->org_len, scale ); +# endif + +#ifdef DEBUG_HINTER + if (ps1_debug_hint_func) + ps1_debug_hint_func( hint, vertical ); +#endif + } + } + } + + return 0; + } + + + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** POINTS INTERPOLATION ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#define PSH1_ZONE_MIN -3200000 +#define PSH1_ZONE_MAX +3200000 + + +#define xxDEBUG_ZONES + +#ifdef DEBUG_ZONES + +#include + + static void + print_zone( PSH1_Zone zone ) + { + printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", + zone->scale/65536.0, + zone->delta/64.0, + zone->min, + zone->max ); + } + +#else +# define print_zone(x) do { } while (0) +#endif + + /* setup interpolation zones once the hints have been grid-fitted */ + /* by the optimizer.. */ + static void + psh1_hint_table_setup_zones( PSH1_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta ) + { + FT_UInt count; + PSH1_Zone zone; + PSH1_Hint *sort, hint, hint2; + + zone = table->zones; + + /* special case, no hints defined */ + if ( table->num_hints == 0 ) + { + zone->scale = scale; + zone->delta = delta; + zone->min = PSH1_ZONE_MIN; + zone->max = PSH1_ZONE_MAX; + + table->num_zones = 1; + table->zone = zone; + return; + } + + /* the first zone is before the first hint */ + /* x' = (x-x0)*s + x0' = x*s + ( x0' - x0*s ) */ + sort = table->sort; + hint = sort[0]; + + zone->scale = scale; + zone->delta = hint->cur_pos - FT_MulFix( hint->org_pos, scale ); + zone->min = PSH1_ZONE_MIN; + zone->max = hint->org_pos; + + print_zone( zone ); + + zone++; + + for ( count = table->num_hints; count > 0; count-- ) + { + FT_Fixed scale2; + + if ( hint->org_len > 0 ) + { + /* setup a zone for inner-stem interpolation */ + /* (x' - x0') = (x - x0)*(x1'-x0')/(x1-x0) */ + /* x' = x*s2 + x0' - x0*s2 */ + + scale2 = FT_DivFix( hint->cur_len, hint->org_len ); + zone->scale = scale2; + zone->min = hint->org_pos; + zone->max = hint->org_pos + hint->org_len; + zone->delta = hint->cur_pos - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + } + + if ( count == 1 ) + break; + + sort++; + hint2 = sort[0]; + + /* setup zone for inter-stem interpolation */ + /* (x'-x1') = (x-x1)*(x2'-x1')/(x2-x1) */ + /* x' = x*s3 + x1' - x1*s3 */ + scale2 = FT_DivFix( hint2->cur_pos - (hint->cur_pos + hint->cur_len), + hint2->org_pos - (hint->org_pos + hint->org_len) ); + zone->scale = scale2; + zone->min = hint->org_pos + hint->org_len; + zone->max = hint2->org_pos; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + + hint = hint2; + } + + /* the last zone */ + zone->scale = scale; + zone->min = hint->org_pos + hint->org_len; + zone->max = PSH1_ZONE_MAX; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale ); + + print_zone( zone ); + + zone++; + + table->num_zones = zone - table->zones; + table->zone = table->zones; + } + + + /* tune a single coordinate with the current interpolation zones */ + static FT_Pos + psh1_hint_table_tune_coord( PSH1_Hint_Table table, + FT_Int coord ) + { + PSH1_Zone zone; + + zone = table->zone; + + if ( coord < zone->min ) + { + do + { + if ( zone == table->zones ) + break; + + zone--; + } + while ( coord < zone->min ); + table->zone = zone; + } + else if ( coord > zone->max ) + { + do + { + if ( zone == table->zones + table->num_zones - 1 ) + break; + + zone++; + } + while ( coord > zone->max ); + table->zone = zone; + } + + return FT_MulFix( coord, zone->scale ) + zone->delta; + } + + + /* tune a given outline with current interpolation zones */ + /* the function only works in a single dimension.. */ + static void + psh1_hint_table_tune_outline( PSH1_Hint_Table table, + FT_Outline* outline, + PSH_Globals globals, + FT_Bool vertical ) + + { + FT_UInt count, first, last; + PS_Mask_Table hint_masks = table->hint_masks; + PS_Mask mask; + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + if ( hint_masks && hint_masks->num_masks > 0 ) + { + first = 0; + mask = hint_masks->masks; + count = hint_masks->num_masks; + for ( ; count > 0; count--, mask++ ) + { + last = mask->end_point; + + if ( last > first ) + { + FT_Vector* vec; + FT_Int count2; + + psh1_hint_table_activate_mask( table, mask ); + psh1_hint_table_optimize( table, globals, outline, vertical ); + psh1_hint_table_setup_zones( table, scale, delta ); + last = mask->end_point; + + vec = outline->points + first; + count2 = last - first; + for ( ; count2 > 0; count2--, vec++ ) + { + FT_Pos x, *px; + + px = vertical ? &vec->x : &vec->y; + x = *px; + + *px = psh1_hint_table_tune_coord( table, (FT_Int)x ); + } + } + + first = last; + } + } + else /* no hints in this glyph, simply scale the outline */ + { + FT_Vector* vec; + + vec = outline->points; + count = outline->n_points; + + if ( vertical ) + { + for ( ; count > 0; count--, vec++ ) + vec->x = FT_MulFix( vec->x, scale ) + delta; + } + else + { + for ( ; count > 0; count--, vec++ ) + vec->y = FT_MulFix( vec->y, scale ) + delta; + } + } + } + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HIGH-LEVEL INTERFACE *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + FT_Error + ps1_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ) + { + PSH1_Hint_TableRec hints; + FT_Error error; + FT_Int dimension; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + PS_Dimension dim = &ps_hints->dimension[dimension]; + + /* initialise hints table */ + memset( &hints, 0, sizeof(hints) ); + error = psh1_hint_table_init( &hints, + &dim->hints, + &dim->masks, + &dim->counters, + ps_hints->memory ); + if (error) goto Exit; + + psh1_hint_table_tune_outline( &hints, + outline, + globals, + dimension ); + + psh1_hint_table_done( &hints, ps_hints->memory ); + } + + Exit: + return error; + } diff --git a/src/pshinter/pshalgo1.h b/src/pshinter/pshalgo1.h new file mode 100644 index 000000000..1b0dd5dc2 --- /dev/null +++ b/src/pshinter/pshalgo1.h @@ -0,0 +1,100 @@ +/***************************************************************************/ +/* */ +/* pshalgo1.h */ +/* */ +/* First (basic) Postscript hinting routines */ +/* */ +/* Copyright 2001 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 __PS_HINTER_ALGO1_H__ +#define __PS_HINTER_ALGO1_H__ + +#include "pshrec.h" + +FT_BEGIN_HEADER + + typedef struct PSH1_HintRec_* PSH1_Hint; + + typedef enum + { + PSH1_HINT_FLAG_GHOST = PS_HINT_FLAG_GHOST, + PSH1_HINT_FLAG_BOTTOM = PS_HINT_FLAG_BOTTOM, + PSH1_HINT_FLAG_ACTIVE = 4 + + } PSH1_Hint_Flags; + +#define psh1_hint_is_active(x) (((x)->flags & PSH1_HINT_FLAG_ACTIVE) != 0) +#define psh1_hint_is_ghost(x) (((x)->flags & PSH1_HINT_FLAG_GHOST ) != 0) + +#define psh1_hint_activate(x) (x)->flags |= PSH1_HINT_FLAG_ACTIVE +#define psh1_hint_deactivate(x) (x)->flags &= ~PSH1_HINT_FLAG_ACTIVE + + typedef struct PSH1_HintRec_ + { + FT_Int org_pos; + FT_Int org_len; + FT_Pos cur_pos; + FT_Pos cur_len; + + FT_UInt flags; + + PSH1_Hint parent; + FT_Int order; + + } PSH1_HintRec; + + + /* this is an interpolation zone used for strong points */ + /* weak points are interpolated according to their strong */ + /* neighbours.. */ + typedef struct PSH1_ZoneRec_ + { + FT_Fixed scale; + FT_Fixed delta; + FT_Pos min; + FT_Pos max; + + } PSH1_ZoneRec, *PSH1_Zone; + + + typedef struct PSH1_Hint_TableRec_ + { + FT_UInt max_hints; + FT_UInt num_hints; + PSH1_Hint hints; + PSH1_Hint* sort; + PSH1_Hint* sort_global; + FT_UInt num_zones; + PSH1_Zone zones; + PSH1_Zone zone; + PS_Mask_Table hint_masks; + PS_Mask_Table counter_masks; + + } PSH1_Hint_TableRec, *PSH1_Hint_Table; + + + extern FT_Error + ps1_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ); + + +#ifdef DEBUG_HINTER + extern PSH1_Hint_Table ps1_debug_hint_table; + + typedef void (*PSH1_HintFunc)( PSH1_Hint hint, FT_Bool vertical ); + extern PSH1_HintFunc ps1_debug_hint_func; +#endif + +FT_END_HEADER + +#endif /* __PS_HINTER_FITTER_H__ */ diff --git a/src/pshinter/pshalgo2.c b/src/pshinter/pshalgo2.c new file mode 100644 index 000000000..c98aa78d2 --- /dev/null +++ b/src/pshinter/pshalgo2.c @@ -0,0 +1,1484 @@ +#include +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include "pshalgo2.h" + + +#ifdef DEBUG_HINTER + extern PSH2_Hint_Table ps2_debug_hint_table = 0; + extern PSH2_HintFunc ps2_debug_hint_func = 0; + extern PSH2_Glyph ps2_debug_glyph = 0; +#endif + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** BASIC HINTS RECORDINGS *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + /* return true iff two stem hints overlap */ + static FT_Int + psh2_hint_overlap( PSH2_Hint hint1, + PSH2_Hint hint2 ) + { + return ( hint1->org_pos + hint1->org_len >= hint2->org_pos && + hint2->org_pos + hint2->org_len >= hint1->org_pos ); + } + + + /* destroy hints table */ + static void + psh2_hint_table_done( PSH2_Hint_Table table, + FT_Memory memory ) + { + FREE( table->zones ); + table->num_zones = 0; + table->zone = 0; + + FREE( table->sort ); + FREE( table->hints ); + table->num_hints = 0; + table->max_hints = 0; + table->sort_global = 0; + } + + + /* deactivate all hints in a table */ + static void + psh2_hint_table_deactivate( PSH2_Hint_Table table ) + { + FT_UInt count = table->max_hints; + PSH2_Hint hint = table->hints; + + for ( ; count > 0; count--, hint++ ) + { + psh2_hint_deactivate(hint); + hint->order = -1; + } + } + + + /* internal function used to record a new hint */ + static void + psh2_hint_table_record( PSH2_Hint_Table table, + FT_UInt index ) + { + PSH2_Hint hint = table->hints + index; + + if ( index >= table->max_hints ) + { + FT_ERROR(( "%s.activate: invalid hint index %d\n", index )); + return; + } + + /* ignore active hints */ + if ( psh2_hint_is_active(hint) ) + return; + + psh2_hint_activate(hint); + + /* now scan the current active hint set in order to determine */ + /* if we're overlapping with another segment.. */ + { + PSH2_Hint* sorted = table->sort_global; + FT_UInt count = table->num_hints; + PSH2_Hint hint2; + + hint->parent = 0; + for ( ; count > 0; count--, sorted++ ) + { + hint2 = sorted[0]; + + if ( psh2_hint_overlap( hint, hint2 ) ) + { + hint->parent = hint2; + break; + } + } + } + + if ( table->num_hints < table->max_hints ) + table->sort_global[ table->num_hints++ ] = hint; + else + { + FT_ERROR(( "%s.activate: too many sorted hints !! BUG !!\n", + "ps.fitter" )); + } + } + + + static void + psh2_hint_table_record_mask( PSH2_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit; + + limit = hint_mask->num_bits; + + if ( limit != table->max_hints ) + { + FT_ERROR(( "%s.activate_mask: invalid bit count (%d instead of %d)\n", + "ps.fitter", hint_mask->num_bits, table->max_hints )); + } + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + psh2_hint_table_record( table, index ); + + mask >>= 1; + } + } + + + /* create hints table */ + static FT_Error + psh2_hint_table_init( PSH2_Hint_Table table, + PS_Hint_Table hints, + PS_Mask_Table hint_masks, + PS_Mask_Table counter_masks, + FT_Memory memory ) + { + FT_UInt count = hints->num_hints; + FT_Error error; + + FT_UNUSED(counter_masks); + + /* allocate our tables */ + if ( ALLOC_ARRAY( table->sort, 2*count, PSH2_Hint ) || + ALLOC_ARRAY( table->hints, count, PSH2_HintRec ) || + ALLOC_ARRAY( table->zones, 2*count+1, PSH2_ZoneRec ) ) + goto Exit; + + table->max_hints = count; + table->sort_global = table->sort + count; + table->num_hints = 0; + table->num_zones = 0; + table->zone = 0; + + /* now, initialise the "hints" array */ + { + PSH2_Hint write = table->hints; + PS_Hint read = hints->hints; + + for ( ; count > 0; count--, write++, read++ ) + { + write->org_pos = read->pos; + write->org_len = read->len; + write->flags = read->flags; + } + } + + /* we now need to determine the initial "parent" stems, first */ + /* activate the hints that are given by the initial hint masks */ + if ( hint_masks ) + { + FT_UInt count = hint_masks->num_masks; + PS_Mask mask = hint_masks->masks; + + table->hint_masks = hint_masks; + + for ( ; count > 0; count--, mask++ ) + psh2_hint_table_record_mask( table, mask ); + } + + /* now, do a linear parse in case some hints were left alone */ + if ( table->num_hints != table->max_hints ) + { + FT_UInt index, count; + + FT_ERROR(( "%s.init: missing/incorrect hint masks !!\n" )); + count = table->max_hints; + for ( index = 0; index < count; index++ ) + psh2_hint_table_record( table, index ); + } + + Exit: + return error; + } + + + + static void + psh2_hint_table_activate_mask( PSH2_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit, count; + + limit = hint_mask->num_bits; + count = 0; + + psh2_hint_table_deactivate( table ); + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + { + PSH2_Hint hint = &table->hints[index]; + + if ( !psh2_hint_is_active(hint) ) + { + PSH2_Hint* sort = table->sort; + FT_UInt count2; + PSH2_Hint hint2; + +#if 0 + for ( count2 = count; count2 > 0; count2--, sort++ ) + { + hint2 = sort[0]; + if ( psh2_hint_overlap( hint, hint2 ) ) + { + FT_ERROR(( "%s.activate_mask: found overlapping hints\n", + "psf.hint" )); + } + } +#else + count2 = 0; +#endif + + if ( count2 == 0 ) + { + psh2_hint_activate( hint ); + if ( count < table->max_hints ) + table->sort[count++] = hint; + else + { + FT_ERROR(( "%s.activate_mask: too many active hints\n", + "psf.hint" )); + } + } + } + } + + mask >>= 1; + } + table->num_hints = count; + + /* now, sort the hints, they're guaranteed to not overlap */ + /* so we can compare their "org_pos" field directly.. */ + { + FT_Int i1, i2; + PSH2_Hint hint1, hint2; + PSH2_Hint* sort = table->sort; + + /* a simple bubble sort will do, since in 99% of cases, the hints */ + /* will be already sorted.. and the sort will be linear */ + for ( i1 = 1; i1 < (FT_Int)count; i1++ ) + { + hint1 = sort[i1]; + for ( i2 = i1-1; i2 >= 0; i2-- ) + { + hint2 = sort[i2]; + if ( hint2->org_pos < hint1->org_pos ) + break; + + sort[i1] = hint2; + sort[i2] = hint1; + } + } + } + } + + + + + + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HINTS GRID-FITTING AND OPTIMISATION *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#ifdef DEBUG_HINTER + static void + ps_simple_scale( PSH2_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta, + FT_Bool vertical ) + { + PSH2_Hint hint; + FT_UInt count; + + for ( count = 0; count < table->max_hints; count++ ) + { + hint = table->hints + count; + + hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; + hint->cur_len = FT_MulFix( hint->org_len, scale ); + + if (ps2_debug_hint_func) + ps2_debug_hint_func( hint, vertical ); + } + } +#endif + + + static void + psh2_hint_align( PSH2_Hint hint, + PSH_Globals globals, + FT_Bool vertical ) + { + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + if ( !psh2_hint_is_fitted(hint) ) + { + FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; + FT_Pos len = FT_MulFix( hint->org_len, scale ); + + FT_Pos fit_center; + FT_Pos fit_len; + + PSH_AlignmentRec align; + + /* compute fitted width/height */ + fit_len = 0; + if ( hint->org_len ) + { + fit_len = psh_dimension_snap_width( dim, hint->org_len ); + if ( fit_len < 64 ) + fit_len = 64; + else + fit_len = (fit_len + 32 ) & -64; + } + + hint->cur_len = fit_len; + + /* check blue zones for horizontal stems */ + align.align = 0; + if (!vertical) + { + psh_blues_snap_stem( &globals->blues, + hint->org_pos + hint->org_len, + hint->org_pos, + &align ); + } + + switch (align.align) + { + case PSH_BLUE_ALIGN_TOP: + { + /* the top of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_top - fit_len; + break; + } + + case PSH_BLUE_ALIGN_BOT: + { + /* the bottom of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_bot; + break; + } + + case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: + { + /* both edges of the stem are aligned against blue zones */ + hint->cur_pos = align.align_bot; + hint->cur_len = align.align_top - align.align_bot; + break; + } + + default: + { + PSH2_Hint parent = hint->parent; + + if ( parent ) + { + FT_Pos par_org_center, par_cur_center; + FT_Pos cur_org_center, cur_delta; + + /* ensure that parent is already fitted */ + if ( !psh2_hint_is_fitted(parent) ) + psh2_hint_align( parent, globals, vertical ); + + par_org_center = parent->org_pos + (parent->org_len/2); + par_cur_center = parent->cur_pos + (parent->cur_len/2); + cur_org_center = hint->org_pos + (hint->org_len/2); + + cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); +#if 0 + if ( cur_delta >= 0 ) + cur_delta = (cur_delta+16) & -64; + else + cur_delta = -((-cur_delta+16) & -64); +#endif + pos = par_cur_center + cur_delta - (len >> 1); + } + + /* normal processing */ + if ( (fit_len/64) & 1 ) + { + /* odd number of pixels */ + fit_center = ((pos + (len >> 1)) & -64) + 32; + } + else + { + /* even number of pixels */ + fit_center = (pos + (len >> 1) + 32) & -64; + } + + hint->cur_pos = fit_center - (fit_len >> 1); + } + } + + psh2_hint_set_fitted(hint); + +#ifdef DEBUG_HINTER + if (ps2_debug_hint_func) + ps2_debug_hint_func( hint, vertical ); +#endif + } + } + + + static void + psh2_hint_table_align_hints( PSH2_Hint_Table table, + PSH_Globals globals, + FT_Bool vertical ) + { + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + PSH2_Hint hint; + FT_UInt count; + +#ifdef DEBUG_HINTER + if ( ps_debug_no_vert_hints && vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return; + } + + if ( ps_debug_no_horz_hints && !vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return; + } +#endif + + hint = table->hints; + count = table->max_hints; + for ( ; count > 0; count--, hint++ ) + psh2_hint_align( hint, globals, vertical ); + } + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** POINTS INTERPOLATION ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#define PSH2_ZONE_MIN -3200000 +#define PSH2_ZONE_MAX +3200000 + + +#define xxDEBUG_ZONES + +#ifdef DEBUG_ZONES + +#include + + static void + print_zone( PSH2_Zone zone ) + { + printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", + zone->scale/65536.0, + zone->delta/64.0, + zone->min, + zone->max ); + } + +#else +# define print_zone(x) do { } while (0) +#endif + + /* setup interpolation zones once the hints have been grid-fitted */ + /* by the optimizer.. */ + static void + psh2_hint_table_setup_zones( PSH2_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta ) + { + FT_UInt count; + PSH2_Zone zone; + PSH2_Hint *sort, hint, hint2; + + zone = table->zones; + + /* special case, no hints defined */ + if ( table->num_hints == 0 ) + { + zone->scale = scale; + zone->delta = delta; + zone->min = PSH2_ZONE_MIN; + zone->max = PSH2_ZONE_MAX; + + table->num_zones = 1; + table->zone = zone; + return; + } + + /* the first zone is before the first hint */ + /* x' = (x-x0)*s + x0' = x*s + ( x0' - x0*s ) */ + sort = table->sort; + hint = sort[0]; + + zone->scale = scale; + zone->delta = hint->cur_pos - FT_MulFix( hint->org_pos, scale ); + zone->min = PSH2_ZONE_MIN; + zone->max = hint->org_pos; + + print_zone( zone ); + + zone++; + + for ( count = table->num_hints; count > 0; count-- ) + { + FT_Fixed scale2; + + if ( hint->org_len > 0 ) + { + /* setup a zone for inner-stem interpolation */ + /* (x' - x0') = (x - x0)*(x1'-x0')/(x1-x0) */ + /* x' = x*s2 + x0' - x0*s2 */ + + scale2 = FT_DivFix( hint->cur_len, hint->org_len ); + zone->scale = scale2; + zone->min = hint->org_pos; + zone->max = hint->org_pos + hint->org_len; + zone->delta = hint->cur_pos - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + } + + if ( count == 1 ) + break; + + sort++; + hint2 = sort[0]; + + /* setup zone for inter-stem interpolation */ + /* (x'-x1') = (x-x1)*(x2'-x1')/(x2-x1) */ + /* x' = x*s3 + x1' - x1*s3 */ + scale2 = FT_DivFix( hint2->cur_pos - (hint->cur_pos + hint->cur_len), + hint2->org_pos - (hint->org_pos + hint->org_len) ); + zone->scale = scale2; + zone->min = hint->org_pos + hint->org_len; + zone->max = hint2->org_pos; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + + hint = hint2; + } + + /* the last zone */ + zone->scale = scale; + zone->min = hint->org_pos + hint->org_len; + zone->max = PSH2_ZONE_MAX; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale ); + + print_zone( zone ); + + zone++; + + table->num_zones = zone - table->zones; + table->zone = table->zones; + } + + + /* tune a single coordinate with the current interpolation zones */ + static FT_Pos + psh2_hint_table_tune_coord( PSH2_Hint_Table table, + FT_Int coord ) + { + PSH2_Zone zone; + + zone = table->zone; + + if ( coord < zone->min ) + { + do + { + if ( zone == table->zones ) + break; + + zone--; + } + while ( coord < zone->min ); + table->zone = zone; + } + else if ( coord > zone->max ) + { + do + { + if ( zone == table->zones + table->num_zones - 1 ) + break; + + zone++; + } + while ( coord > zone->max ); + table->zone = zone; + } + + return FT_MulFix( coord, zone->scale ) + zone->delta; + } + + +#if 0 + /* tune a given outline with current interpolation zones */ + /* the function only works in a single dimension.. */ + static void + psh2_hint_table_tune_outline( PSH2_Hint_Table table, + FT_Outline* outline, + PSH_Globals globals, + FT_Bool vertical ) + + { + FT_UInt count, first, last; + PS_Mask_Table hint_masks = table->hint_masks; + PS_Mask mask; + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + if ( hint_masks && hint_masks->num_masks > 0 ) + { + first = 0; + mask = hint_masks->masks; + count = hint_masks->num_masks; + for ( ; count > 0; count--, mask++ ) + { + last = mask->end_point; + + if ( last > first ) + { + FT_Vector* vec; + FT_Int count2; + + psh2_hint_table_activate_mask( table, mask ); + psh2_hint_table_optimize( table, globals, outline, vertical ); + psh2_hint_table_setup_zones( table, scale, delta ); + last = mask->end_point; + + vec = outline->points + first; + count2 = last - first; + for ( ; count2 > 0; count2--, vec++ ) + { + FT_Pos x, *px; + + px = vertical ? &vec->x : &vec->y; + x = *px; + + *px = psh2_hint_table_tune_coord( table, (FT_Int)x ); + } + } + + first = last; + } + } + else /* no hints in this glyph, simply scale the outline */ + { + FT_Vector* vec; + + vec = outline->points; + count = outline->n_points; + + if ( vertical ) + { + for ( ; count > 0; count--, vec++ ) + vec->x = FT_MulFix( vec->x, scale ) + delta; + } + else + { + for ( ; count > 0; count--, vec++ ) + vec->y = FT_MulFix( vec->y, scale ) + delta; + } + } + } +#endif + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HINTER GLYPH MANAGEMENT *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + static int + psh2_point_is_extremum( PSH2_Point point ) + { + PSH2_Point before = point; + PSH2_Point after = point; + FT_Pos d_before; + FT_Pos d_after; + + do + { + before = before->prev; + if ( before == point ) + return 0; + + d_before = before->org_u - point->org_u; + } + while ( d_before == 0 ); + + do + { + after = after->next; + if ( after == point ) + return 0; + + d_after = after->org_u - point->org_u; + } + while ( d_after == 0 ); + + return ( ( d_before > 0 && d_after > 0 ) || + ( d_before < 0 && d_after < 0 ) ); + } + + + + static void + psh2_glyph_done( PSH2_Glyph glyph ) + { + FT_Memory memory = glyph->memory; + + psh2_hint_table_done( &glyph->hint_tables[1], memory ); + psh2_hint_table_done( &glyph->hint_tables[0], memory ); + + FREE( glyph->points ); + FREE( glyph->contours ); + + glyph->num_points = 0; + glyph->num_contours = 0; + + glyph->memory = 0; + } + + + static int + psh2_compute_dir( FT_Pos dx, FT_Pos dy ) + { + FT_Pos ax, ay; + int result = PSH2_DIR_NONE; + + ax = ( dx >= 0 ) ? dx : -dx; + ay = ( dy >= 0 ) ? dy : -dy; + + if ( ay*12 < ax ) + { + /* |dy| <<< |dx| means a near-horizontal segment */ + result = ( dx >= 0 ) ? PSH2_DIR_RIGHT : PSH2_DIR_LEFT; + } + else if ( ax*12 < ay ) + { + /* |dx| <<< |dy| means a near-vertical segment */ + result = ( dy >= 0 ) ? PSH2_DIR_UP : PSH2_DIR_DOWN; + } + return result; + } + + + static FT_Error + psh2_glyph_init( PSH2_Glyph glyph, + FT_Outline* outline, + PS_Hints ps_hints, + PSH_Globals globals ) + { + FT_Error error; + FT_Memory memory; + + /* clear all fields */ + memset( glyph, 0, sizeof(*glyph) ); + + memory = globals->memory; + + /* allocate and setup points + contours arrays */ + if ( ALLOC_ARRAY( glyph->points, outline->n_points, PSH2_PointRec ) || + ALLOC_ARRAY( glyph->contours, outline->n_contours, PSH2_ContourRec ) ) + goto Exit; + + glyph->num_points = outline->n_points; + glyph->num_contours = outline->n_contours; + + { + FT_UInt first = 0, next, n; + PSH2_Point points = glyph->points; + PSH2_Contour contour = glyph->contours; + + for ( n = 0; n < glyph->num_contours; n++ ) + { + FT_Int count; + PSH2_Point point; + + next = outline->contours[n] + 1; + count = next - first; + + contour->start = points + first; + contour->count = (FT_UInt)count; + + if ( count > 0 ) + { + point = points + first; + + point->prev = points + next - 1; + point->contour = contour; + for ( ; count > 1; count-- ) + { + point[0].next = point + 1; + point[1].prev = point; + point++; + point->contour = contour; + } + point->next = points + first; + } + + contour++; + first = next; + } + } + + { + PSH2_Point points = glyph->points; + PSH2_Point point = points; + FT_Vector* vec = outline->points; + FT_UInt n; + + for ( n = 0; n < glyph->num_points; n++, point++ ) + { + FT_Int n_prev = point->prev - points; + FT_Int n_next = point->next - points; + FT_Pos dxi, dyi, dxo, dyo; + + if ( !(outline->tags[n] & FT_Curve_Tag_On) ) + point->flags = PSH2_POINT_OFF; + + dxi = vec[n].x - vec[n_prev].x; + dyi = vec[n].y - vec[n_prev].y; + + point->dir_in = psh2_compute_dir( dxi, dyi ); + + dxo = vec[n_next].x - vec[n].x; + dyo = vec[n_next].y - vec[n].y; + + point->dir_out = psh2_compute_dir( dxo, dyo ); + + /* detect smooth points */ + if ( point->flags & PSH2_POINT_OFF ) + { + point->flags |= PSH2_POINT_SMOOTH; + } + else if ( point->dir_in != PSH2_DIR_NONE || + point->dir_out != PSH2_DIR_NONE ) + { + if ( point->dir_in == point->dir_out ) + point->flags |= PSH2_POINT_SMOOTH; + } + else + { + FT_Angle angle_in, angle_out, diff; + + angle_in = FT_Atan2( dxi, dyi ); + angle_out = FT_Atan2( dxo, dyo ); + + diff = angle_in - angle_out; + if ( diff < 0 ) + diff = -diff; + + if ( diff > FT_ANGLE_PI ) + diff = FT_ANGLE_2PI - diff; + + if ( (diff < FT_ANGLE_PI/16) ) + point->flags |= PSH2_POINT_SMOOTH; + } + } + } + + glyph->memory = memory; + glyph->outline = outline; + glyph->globals = globals; + + /* now deal with hints tables */ + error = psh2_hint_table_init( &glyph->hint_tables [0], + &ps_hints->dimension[0].hints, + &ps_hints->dimension[0].masks, + &ps_hints->dimension[0].counters, + memory ); + if (error) goto Exit; + + error = psh2_hint_table_init( &glyph->hint_tables [1], + &ps_hints->dimension[1].hints, + &ps_hints->dimension[1].masks, + &ps_hints->dimension[1].counters, + memory ); + if (error) goto Exit; + + Exit: + return error; + } + + + /* load outline point coordinates into hinter glyph */ + static void + psh2_glyph_load_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + FT_Vector* vec = glyph->outline->points; + PSH2_Point point = glyph->points; + FT_UInt count = glyph->num_points; + + for ( ; count > 0; count--, point++, vec++ ) + { + point->flags &= PSH2_POINT_OFF | PSH2_POINT_SMOOTH; + point->hint = 0; + if (vertical) + point->org_u = vec->x; + else + point->org_u = vec->y; + +#ifdef DEBUG_HINTER + point->org_x = vec->x; + point->org_y = vec->y; +#endif + } + } + + + /* save hinted point coordinates back to outline */ + static void + psh2_glyph_save_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + FT_UInt n; + PSH2_Point point = glyph->points; + FT_Vector* vec = glyph->outline->points; + char* tags = glyph->outline->tags; + + for ( n = 0; n < glyph->num_points; n++ ) + { + if (vertical) + vec[n].x = point->cur_u; + else + vec[n].y = point->cur_u; + + if ( psh2_point_is_strong(point) ) + tags[n] |= vertical ? 32 : 64; + +#ifdef DEBUG_HINTER + if (vertical) + { + point->cur_x = point->cur_u; + point->flags_x = point->flags; + } + else + { + point->cur_y = point->cur_u; + point->flags_y = point->flags; + } +#endif + point++; + } + } + + + static void + psh2_hint_table_find_strong_point( PSH2_Hint_Table table, + PSH2_Point point, + FT_Int major_dir ) + { + PSH2_Hint* sort = table->sort; + FT_UInt num_hints = table->num_hints; + + for ( ; num_hints > 0; num_hints--, sort++ ) + { + PSH2_Hint hint = sort[0]; + + if ( ABS(point->dir_in) == major_dir || + ABS(point->dir_out) == major_dir ) + { + FT_Pos d; + + d = point->org_u - hint->org_pos; + if ( ABS(d) < 3 ) + { + Is_Strong: + psh2_point_set_strong(point); + point->hint = hint; + break; + } + + d -= hint->org_len; + if ( ABS(d) < 3 ) + goto Is_Strong; + } + +#if 1 + if ( point->org_u >= hint->org_pos && + point->org_u <= hint->org_pos + hint->org_len && + psh2_point_is_extremum( point ) ) + { + /* attach to hint, but don't mark as strong */ + point->hint = hint; + break; + } +#endif + } + } + + + + /* find strong points in a glyph */ + static void + psh2_glyph_find_strong_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + /* a point is strong if it is located on a stem */ + /* edge and has an "in" or "out" tangent to the hint's direction */ + { + PSH2_Hint_Table table = &glyph->hint_tables[vertical]; + PS_Mask mask = table->hint_masks->masks; + FT_UInt num_masks = table->hint_masks->num_masks; + FT_UInt first = 0; + FT_Int major_dir = vertical ? PSH2_DIR_UP : PSH2_DIR_RIGHT; + FT_Int minor_dir = vertical ? PSH2_DIR_RIGHT : PSH2_DIR_UP; + + /* process secondary hints to "selected" points */ + if ( num_masks > 1 ) + { + mask++; + for ( ; num_masks > 1; num_masks--, mask++ ) + { + FT_UInt next; + FT_Int count; + + next = mask->end_point; + count = next - first; + if ( count > 0 ) + { + PSH2_Point point = glyph->points + first; + + psh2_hint_table_activate_mask( table, mask ); + + for ( ; count > 0; count--, point++ ) + psh2_hint_table_find_strong_point( table, point, major_dir ); + } + first = next; + } + } + + /* process primary hints for all points */ + if ( num_masks == 1 ) + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + psh2_hint_table_activate_mask( table, table->hint_masks->masks ); + for ( ; count > 0; count--, point++ ) + { + if ( !psh2_point_is_strong(point) ) + psh2_hint_table_find_strong_point( table, point, major_dir ); + } + } + + /* now, certain points may have been attached to hint and */ + /* not marked as strong, update their flags then.. */ + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + for ( ; count > 0; count--, point++ ) + if ( point->hint && !psh2_point_is_strong(point) ) + psh2_point_set_strong(point); + } + } + } + + + + /* interpolate strong points with the help of hinted coordinates */ + static void + psh2_glyph_interpolate_strong_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + PSH_Dimension dim = &glyph->globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + for ( ; count > 0; count--, point++ ) + { + PSH2_Hint hint = point->hint; + + if ( hint ) + { + FT_Pos delta; + + delta = point->org_u - hint->org_pos; + + if ( delta <= 0 ) + point->cur_u = hint->cur_pos + FT_MulFix( delta, scale ); + + else if ( delta >= hint->org_len ) + point->cur_u = hint->cur_pos + hint->cur_len + + FT_MulFix( delta - hint->org_len, scale ); + + else if ( hint->org_len > 0 ) + point->cur_u = hint->cur_pos + + FT_MulDiv( delta, hint->cur_len, hint->org_len ); + else + point->cur_u = hint->cur_pos; + + psh2_point_set_fitted(point); + } + } + } + } + + + static void + psh2_glyph_interpolate_normal_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { +#if 1 + PSH_Dimension dim = &glyph->globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + + /* first technique: a point is strong if it is a local extrema */ + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + for ( ; count > 0; count--, point++ ) + { + if ( psh2_point_is_strong(point) ) + continue; + + /* sometimes, some local extremas are smooth points */ + if ( psh2_point_is_smooth(point) ) + { + if ( point->dir_in == PSH2_DIR_NONE || + point->dir_in != point->dir_out ) + continue; + + if ( !psh2_point_is_extremum( point ) ) + continue; + + point->flags &= ~PSH2_POINT_SMOOTH; + } + + /* find best enclosing point coordinates */ + { + PSH2_Point before = 0; + PSH2_Point after = 0; + + FT_Pos diff_before = -32000; + FT_Pos diff_after = 32000; + FT_Pos u = point->org_u; + + FT_Int count2 = glyph->num_points; + PSH2_Point cur = glyph->points; + + for ( ; count2 > 0; count2--, cur++ ) + { + if ( psh2_point_is_strong(cur) ) + { + FT_Pos diff = cur->org_u - u;; + + if ( diff <= 0 ) + { + if ( diff > diff_before ) + { + diff_before = diff; + before = cur; + } + } + else if ( diff >= 0 ) + { + if ( diff < diff_after ) + { + diff_after = diff; + after = cur; + } + } + } + } + + if ( !before ) + { + if ( !after ) + continue; + + /* we're before the first strong point coordinate */ + /* simply translate the point.. */ + point->cur_u = after->cur_u + + FT_MulFix( point->org_u - after->org_u, scale ); + } + else if ( !after ) + { + /* we're after the last strong point coordinate */ + /* simply translate the point.. */ + point->cur_u = before->cur_u + + FT_MulFix( point->org_u - before->org_u, scale ); + } + else + { + if ( diff_before == 0 ) + point->cur_u = before->cur_u; + + else if ( diff_after == 0 ) + point->cur_u = after->cur_u; + + else + point->cur_u = before->cur_u + + FT_MulDiv( u - before->org_u, + after->cur_u - before->cur_u, + after->org_u - before->org_u ); + } + + psh2_point_set_fitted(point); + } + } + } +#endif + } + + + + /* interpolate other points */ + static void + psh2_glyph_interpolate_other_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + PSH_Dimension dim = &glyph->globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + PSH2_Contour contour = glyph->contours; + FT_UInt num_contours = glyph->num_contours; + + for ( ; num_contours > 0; num_contours--, contour++ ) + { + PSH2_Point start = contour->start; + PSH2_Point first, next, point; + FT_UInt fit_count; + + /* count the number of strong points in this contour */ + next = start + contour->count; + fit_count = 0; + first = 0; + + for ( point = start; point < next; point++ ) + if ( psh2_point_is_fitted(point) ) + { + if ( !first ) + first = point; + + fit_count++; + } + + /* if there is less than 2 fitted points in the contour, we'll */ + /* simply scale and eventually translate the contour points */ + if ( fit_count < 2 ) + { + if ( fit_count == 1 ) + delta = first->cur_u - FT_MulFix( first->org_u, scale ); + + for ( point = start; point < next; point++ ) + if ( point != first ) + point->cur_u = FT_MulFix( point->org_u, scale ) + delta; + + goto Next_Contour; + } + + /* there are more than 2 strong points in this contour, we'll */ + /* need to interpolate weak points between them.. */ + start = first; + do + { + point = first; + + /* skip consecutive fitted points */ + for (;;) + { + next = first->next; + if ( next == start ) + goto Next_Contour; + + if ( !psh2_point_is_fitted(next) ) + break; + + first = next; + } + + /* find next fitted point after unfitted one */ + for (;;) + { + next = next->next; + if ( psh2_point_is_fitted(next) ) + break; + } + + /* now interpolate between them */ + { + FT_Pos org_a, org_ab, cur_a, cur_ab; + FT_Pos org_c, org_ac, cur_c; + FT_Fixed scale_ab; + + if ( first->org_u <= next->org_u ) + { + org_a = first->org_u; + cur_a = first->cur_u; + org_ab = next->org_u - org_a; + cur_ab = next->cur_u - cur_a; + } + else + { + org_a = next->org_u; + cur_a = next->cur_u; + org_ab = first->org_u - org_a; + cur_ab = first->cur_u - cur_a; + } + + scale_ab = 0x10000L; + if ( org_ab > 0 ) + scale_ab = FT_DivFix( cur_ab, org_ab ); + + point = first->next; + do + { + org_c = point->org_u; + org_ac = org_c - org_a; + + if ( org_ac <= 0 ) + { + /* on the left of the interpolation zone */ + cur_c = cur_a + FT_MulFix( org_ac, scale ); + } + else if ( org_ac >= org_ab ) + { + /* on the right on the interpolation zone */ + cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale ); + } + else + { + /* within the interpolation zone */ + cur_c = cur_a + FT_MulFix( org_ac, scale_ab ); + } + + point->cur_u = cur_c; + + point = point->next; + } + while ( point != next ); + } + + /* keep going until all points in the contours have been processed */ + first = next; + } + while ( first != start ); + + Next_Contour: + ; + } + } + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HIGH-LEVEL INTERFACE *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + FT_Error + ps2_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ) + { + PSH2_Glyph glyph; + PSH2_GlyphRec glyphrec; + FT_Error error; + FT_Memory memory; + FT_Int dimension; + + memory = globals->memory; + +#ifdef DEBUG_HINTER + if ( ps2_debug_glyph ) + { + psh2_glyph_done( ps2_debug_glyph ); + FREE( ps2_debug_glyph ); + } + + if ( ALLOC( glyph, sizeof(*glyph) ) ) + return error; + + ps2_debug_glyph = glyph; +#else + glyph = &glyphrec; +#endif + + error = psh2_glyph_init( glyph, outline, ps_hints, globals ); + if (error) goto Exit; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + /* load outline coordinates into glyph */ + psh2_glyph_load_points( glyph, dimension ); + + /* compute aligned stem/hints positions */ + psh2_hint_table_align_hints( &glyph->hint_tables[dimension], + glyph->globals, + dimension ); + + /* find strong points, align them, then interpolate others */ + psh2_glyph_find_strong_points( glyph, dimension ); + psh2_glyph_interpolate_strong_points( glyph, dimension ); + psh2_glyph_interpolate_normal_points( glyph, dimension ); + psh2_glyph_interpolate_other_points( glyph, dimension ); + + /* save hinted coordinates back to outline */ + psh2_glyph_save_points( glyph, dimension ); + } + + Exit: +#ifndef DEBUG_HINTER + psh2_glyph_done( glyph ); +#endif + return error; + } diff --git a/src/pshinter/pshalgo2.h b/src/pshinter/pshalgo2.h new file mode 100644 index 000000000..575712cc3 --- /dev/null +++ b/src/pshinter/pshalgo2.h @@ -0,0 +1,185 @@ +/***************************************************************************/ +/* */ +/* pshalgo2.h */ +/* */ +/* First (basic) Postscript hinting routines */ +/* */ +/* Copyright 2001 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 __PS_HINTER_ALGO2_H__ +#define __PS_HINTER_ALGO2_H__ + +#include "pshrec.h" +#include "pshglob.h" +#include FT_TRIGONOMETRY_H + +FT_BEGIN_HEADER + + typedef struct PSH2_HintRec_* PSH2_Hint; + + typedef enum + { + PSH2_HINT_GHOST = PS_HINT_FLAG_GHOST, + PSH2_HINT_BOTTOM = PS_HINT_FLAG_BOTTOM, + PSH2_HINT_ACTIVE = 4, + PSH2_HINT_FITTED = 8 + } PSH2_Hint_Flags; + +#define psh2_hint_is_active(x) (((x)->flags & PSH2_HINT_ACTIVE) != 0) +#define psh2_hint_is_ghost(x) (((x)->flags & PSH2_HINT_GHOST) != 0) +#define psh2_hint_is_fitted(x) (((x)->flags & PSH2_HINT_FITTED) != 0) + +#define psh2_hint_activate(x) (x)->flags |= PSH2_HINT_ACTIVE +#define psh2_hint_deactivate(x) (x)->flags &= ~PSH2_HINT_ACTIVE +#define psh2_hint_set_fitted(x) (x)->flags |= PSH2_HINT_FITTED + + typedef struct PSH2_HintRec_ + { + FT_Int org_pos; + FT_Int org_len; + FT_Pos cur_pos; + FT_Pos cur_len; + FT_UInt flags; + PSH2_Hint parent; + FT_Int order; + + } PSH2_HintRec; + + + /* this is an interpolation zone used for strong points */ + /* weak points are interpolated according to their strong */ + /* neighbours.. */ + typedef struct PSH2_ZoneRec_ + { + FT_Fixed scale; + FT_Fixed delta; + FT_Pos min; + FT_Pos max; + + } PSH2_ZoneRec, *PSH2_Zone; + + + typedef struct PSH2_Hint_TableRec_ + { + FT_UInt max_hints; + FT_UInt num_hints; + PSH2_Hint hints; + PSH2_Hint* sort; + PSH2_Hint* sort_global; + FT_UInt num_zones; + PSH2_Zone zones; + PSH2_Zone zone; + PS_Mask_Table hint_masks; + PS_Mask_Table counter_masks; + + } PSH2_Hint_TableRec, *PSH2_Hint_Table; + + typedef struct PSH2_PointRec_* PSH2_Point; + typedef struct PSH2_ContourRec_* PSH2_Contour; + + enum + { + PSH2_DIR_NONE = 4, + PSH2_DIR_UP = 1, + PSH2_DIR_DOWN = -1, + PSH2_DIR_LEFT = -2, + PSH2_DIR_RIGHT = 2 + }; + + enum + { + PSH2_POINT_OFF = 1, /* point is off the curve */ + PSH2_POINT_STRONG = 2, /* point is strong */ + PSH2_POINT_SMOOTH = 4, /* point is smooth */ + PSH2_POINT_FITTED = 8 /* point is already fitted */ + }; + + + typedef struct PSH2_PointRec_ + { + PSH2_Point prev; + PSH2_Point next; + PSH2_Contour contour; + FT_UInt flags; + FT_Char dir_in; + FT_Char dir_out; + FT_Angle angle_in; + FT_Angle angle_out; + PSH2_Hint hint; + FT_Pos org_u; + FT_Pos cur_u; +#ifdef DEBUG_HINTER + FT_Pos org_x; + FT_Pos cur_x; + FT_Pos org_y; + FT_Pos cur_y; + FT_UInt flags_x; + FT_UInt flags_y; +#endif + + } PSH2_PointRec; + +#define psh2_point_is_strong(p) ((p)->flags & PSH2_POINT_STRONG) +#define psh2_point_is_fitted(p) ((p)->flags & PSH2_POINT_FITTED) +#define psh2_point_is_smooth(p) ((p)->flags & PSH2_POINT_SMOOTH) + +#define psh2_point_set_strong(p) (p)->flags |= PSH2_POINT_STRONG +#define psh2_point_set_fitted(p) (p)->flags |= PSH2_POINT_FITTED +#define psh2_point_set_smooth(p) (p)->flags |= PSH2_POINT_SMOOTH + + typedef struct PSH2_ContourRec_ + { + PSH2_Point start; + FT_UInt count; + + } PSH2_ContourRec; + + + + typedef struct PSH2_GlyphRec_ + { + FT_UInt num_points; + FT_UInt num_contours; + + PSH2_Point points; + PSH2_Contour contours; + + FT_Memory memory; + FT_Outline* outline; + PSH_Globals globals; + PSH2_Hint_TableRec hint_tables[2]; + + FT_Bool vertical; + FT_Int major_dir; + FT_Int minor_dir; + + } PSH2_GlyphRec, *PSH2_Glyph; + + +#ifdef DEBUG_HINTER + extern PSH2_Hint_Table ps2_debug_hint_table; + + typedef void (*PSH2_HintFunc)( PSH2_Hint hint, FT_Bool vertical ); + extern PSH2_HintFunc ps2_debug_hint_func; + + extern PSH2_Glyph ps2_debug_glyph; +#endif + + + extern FT_Error + ps2_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ); + +FT_END_HEADER + +#endif /* __PS_HINTS_ALGO_2_H__ */ diff --git a/src/pshinter/pshfit.c b/src/pshinter/pshfit.c deleted file mode 100644 index 1f9a70bf0..000000000 --- a/src/pshinter/pshfit.c +++ /dev/null @@ -1,555 +0,0 @@ -#include -#include FT_INTERNAL_OBJECTS_H -#include FT_INTERNAL_DEBUG_H -#include "pshfit.h" -#include "pshoptim.h" - - /* return true iff two stem hints overlap */ - static FT_Int - psh_hint_overlap( PSH_Hint hint1, - PSH_Hint hint2 ) - { - return ( hint1->org_pos + hint1->org_len >= hint2->org_pos && - hint2->org_pos + hint2->org_len >= hint1->org_pos ); - } - - - /* destroy hints table */ - static void - psh_hint_table_done( PSH_Hint_Table table, - FT_Memory memory ) - { - FREE( table->zones ); - table->num_zones = 0; - table->zone = 0; - - FREE( table->sort ); - FREE( table->hints ); - table->num_hints = 0; - table->max_hints = 0; - table->sort_global = 0; - } - - - /* deactivate all hints in a table */ - static void - psh_hint_table_deactivate( PSH_Hint_Table table ) - { - FT_UInt count = table->max_hints; - PSH_Hint hint = table->hints; - - for ( ; count > 0; count--, hint++ ) - { - psh_hint_deactivate(hint); - hint->order = -1; - } - } - - - /* internal function used to record a new hint */ - static void - psh_hint_table_record( PSH_Hint_Table table, - FT_UInt index ) - { - PSH_Hint hint = table->hints + index; - - if ( index >= table->max_hints ) - { - FT_ERROR(( "%s.activate: invalid hint index %d\n", index )); - return; - } - - /* ignore active hints */ - if ( psh_hint_is_active(hint) ) - return; - - psh_hint_activate(hint); - - /* now scan the current active hint set in order to determine */ - /* if we're overlapping with another segment.. */ - { - PSH_Hint* sorted = table->sort_global; - FT_UInt count = table->num_hints; - PSH_Hint hint2; - - hint->parent = 0; - for ( ; count > 0; count--, sorted++ ) - { - hint2 = sorted[0]; - - if ( psh_hint_overlap( hint, hint2 ) ) - { - hint->parent = hint2; - break; - } - } - } - - if ( table->num_hints < table->max_hints ) - table->sort_global[ table->num_hints++ ] = hint; - else - { - FT_ERROR(( "%s.activate: too many sorted hints !! BUG !!\n", - "ps.fitter" )); - } - } - - - static void - psh_hint_table_record_mask( PSH_Hint_Table table, - PS_Mask hint_mask ) - { - FT_Int mask = 0, val = 0; - FT_Byte* cursor = hint_mask->bytes; - FT_UInt index, limit; - - limit = hint_mask->num_bits; - - if ( limit != table->max_hints ) - { - FT_ERROR(( "%s.activate_mask: invalid bit count (%d instead of %d)\n", - "ps.fitter", hint_mask->num_bits, table->max_hints )); - } - - for ( index = 0; index < limit; index++ ) - { - if ( mask == 0 ) - { - val = *cursor++; - mask = 0x80; - } - - if ( val & mask ) - psh_hint_table_record( table, index ); - - mask >>= 1; - } - } - - - /* create hints table */ - static FT_Error - psh_hint_table_init( PSH_Hint_Table table, - PS_Hint_Table hints, - PS_Mask_Table hint_masks, - PS_Mask_Table counter_masks, - FT_Memory memory ) - { - FT_UInt count = hints->num_hints; - FT_Error error; - - FT_UNUSED(counter_masks); - - /* allocate our tables */ - if ( ALLOC_ARRAY( table->sort, 2*count, PSH_Hint ) || - ALLOC_ARRAY( table->hints, count, PSH_HintRec ) || - ALLOC_ARRAY( table->zones, 2*count+1, PSH_ZoneRec ) ) - goto Exit; - - table->max_hints = count; - table->sort_global = table->sort + count; - table->num_hints = 0; - table->num_zones = 0; - table->zone = 0; - - /* now, initialise the "hints" array */ - { - PSH_Hint write = table->hints; - PS_Hint read = hints->hints; - - for ( ; count > 0; count--, write++, read++ ) - { - write->org_pos = read->pos; - write->org_len = read->len; - write->flags = read->flags; - } - } - - /* we now need to determine the initial "parent" stems, first */ - /* activate the hints that are given by the initial hint masks */ - if ( hint_masks ) - { - FT_UInt count = hint_masks->num_masks; - PS_Mask mask = hint_masks->masks; - - table->hint_masks = hint_masks; - - for ( ; count > 0; count--, mask++ ) - psh_hint_table_record_mask( table, mask ); - } - - /* now, do a linear parse in case some hints were left alone */ - if ( table->num_hints != table->max_hints ) - { - FT_UInt index, count; - - FT_ERROR(( "%s.init: missing/incorrect hint masks !!\n" )); - count = table->max_hints; - for ( index = 0; index < count; index++ ) - psh_hint_table_record( table, index ); - } - - Exit: - return error; - } - - - - static void - psh_hint_table_activate_mask( PSH_Hint_Table table, - PS_Mask hint_mask ) - { - FT_Int mask = 0, val = 0; - FT_Byte* cursor = hint_mask->bytes; - FT_UInt index, limit, count; - - limit = hint_mask->num_bits; - count = 0; - - psh_hint_table_deactivate( table ); - - for ( index = 0; index < limit; index++ ) - { - if ( mask == 0 ) - { - val = *cursor++; - mask = 0x80; - } - - if ( val & mask ) - { - PSH_Hint hint = &table->hints[index]; - - if ( !psh_hint_is_active(hint) ) - { - PSH_Hint* sort = table->sort; - FT_UInt count2; - PSH_Hint hint2; - - for ( count2 = count; count2 > 0; count2--, sort++ ) - { - hint2 = sort[0]; - if ( psh_hint_overlap( hint, hint2 ) ) - { - FT_ERROR(( "%s.activate_mask: found overlapping hints\n", - "psf.hint" )); - break; - } - } - - if ( count2 == 0 ) - { - psh_hint_activate( hint ); - if ( count < table->max_hints ) - table->sort[count++] = hint; - else - { - FT_ERROR(( "%s.activate_mask: too many active hints\n", - "psf.hint" )); - } - } - } - } - - mask >>= 1; - } - table->num_hints = count; - - /* now, sort the hints, they're guaranteed to not overlap */ - /* so we can compare their "org_pos" field directly.. */ - { - FT_Int i1, i2; - PSH_Hint hint1, hint2; - PSH_Hint* sort = table->sort; - - /* a simple bubble sort will do, since in 99% of cases, the hints */ - /* will be already sorted.. and the sort will be linear */ - for ( i1 = 1; i1 < (FT_Int)count; i1++ ) - { - hint1 = sort[i1]; - for ( i2 = i1-1; i2 >= 0; i2-- ) - { - hint2 = sort[i2]; - if ( hint2->org_pos < hint1->org_pos ) - break; - - sort[i1] = hint2; - sort[i2] = hint1; - } - } - } - } - - -#define PSH_ZONE_MIN -3200000 -#define PSH_ZONE_MAX +3200000 - - -#define xxDEBUG_ZONES - -#ifdef DEBUG_ZONES - -#include - - static void - print_zone( PSH_Zone zone ) - { - printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", - zone->scale/65536.0, - zone->delta/64.0, - zone->min, - zone->max ); - } - -#else -# define print_zone(x) do { } while (0) -#endif - - /* setup interpolation zones once the hints have been grid-fitted */ - /* by the optimizer.. */ - static void - psh_hint_table_setup_zones( PSH_Hint_Table table, - FT_Fixed scale, - FT_Fixed delta ) - { - FT_UInt count; - PSH_Zone zone; - PSH_Hint *sort, hint, hint2; - - zone = table->zones; - - /* special case, no hints defined */ - if ( table->num_hints == 0 ) - { - zone->scale = scale; - zone->delta = delta; - zone->min = PSH_ZONE_MIN; - zone->max = PSH_ZONE_MAX; - table->num_zones = 1; - return; - } - - /* the first zone is before the first hint */ - /* x' = (x-x0)*s + x0' = x*s + ( x0' - x0*s ) */ - sort = table->sort; - hint = sort[0]; - - zone->scale = scale; - zone->delta = hint->cur_pos - FT_MulFix( hint->org_pos, scale ); - zone->min = PSH_ZONE_MIN; - zone->max = hint->org_pos; - - print_zone( zone ); - - zone++; - - for ( count = table->num_hints; count > 0; count-- ) - { - FT_Fixed scale2; - - if ( hint->org_len > 0 ) - { - /* setup a zone for inner-stem interpolation */ - /* (x' - x0') = (x - x0)*(x1'-x0')/(x1-x0) */ - /* x' = x*s2 + x0' - x0*s2 */ - - scale2 = FT_DivFix( hint->cur_len, hint->org_len ); - zone->scale = scale2; - zone->min = hint->org_pos; - zone->max = hint->org_pos + hint->org_len; - zone->delta = hint->cur_pos - FT_MulFix( zone->min, scale2 ); - - print_zone( zone ); - - zone++; - } - - if ( count == 1 ) - break; - - sort++; - hint2 = sort[0]; - - /* setup zone for inter-stem interpolation */ - /* (x'-x1') = (x-x1)*(x2'-x1')/(x2-x1) */ - /* x' = x*s3 + x1' - x1*s3 */ - scale2 = FT_DivFix( hint2->cur_pos - (hint->cur_pos + hint->cur_len), - hint2->org_pos - (hint->org_pos + hint->org_len) ); - zone->scale = scale2; - zone->min = hint->org_pos + hint->org_len; - zone->max = hint2->org_pos; - zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale2 ); - - print_zone( zone ); - - zone++; - - hint = hint2; - } - - /* the last zone */ - zone->scale = scale; - zone->min = hint->org_pos + hint->org_len; - zone->max = PSH_ZONE_MAX; - zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale ); - - print_zone( zone ); - - zone++; - - table->num_zones = zone - table->zones; - table->zone = table->zones; - } - - - /* tune a single coordinate with the current interpolation zones */ - static FT_Pos - psh_hint_table_tune_coord( PSH_Hint_Table table, - FT_Int coord ) - { - PSH_Zone zone; - - zone = table->zone; - - if ( coord < zone->min ) - { - do - { - if ( zone == table->zones ) - break; - - zone--; - } - while ( coord < zone->min ); - table->zone = zone; - } - else if ( coord > zone->max ) - { - do - { - if ( zone == table->zones + table->num_zones - 1 ) - break; - - zone++; - } - while ( coord > zone->max ); - table->zone = zone; - } - - return FT_MulFix( coord, zone->scale ) + zone->delta; - } - - - /* tune a given outline with current interpolation zones */ - /* the function only works in a single dimension.. */ - static void - psh_hint_table_tune_outline( PSH_Hint_Table table, - FT_Outline* outline, - PSH_Globals globals, - FT_Bool vertical ) - - { - FT_UInt count, first, last; - PS_Mask_Table hint_masks = table->hint_masks; - PS_Mask mask; - PSH_Dimension dim = &globals->dimension[vertical]; - FT_Fixed scale = dim->scale_mult; - FT_Fixed delta = dim->scale_delta; - - if ( hint_masks && hint_masks->num_masks > 0 ) - { - first = 0; - mask = hint_masks->masks; - count = hint_masks->num_masks; - for ( ; count > 0; count--, mask++ ) - { - last = mask->end_point; - - if ( last > first ) - { - FT_Vector* vec; - FT_Int count2; - - psh_hint_table_activate_mask( table, mask ); - psh_hint_table_optimize( table, globals, outline, vertical ); - psh_hint_table_setup_zones( table, scale, delta ); - last = mask->end_point; - - vec = outline->points + first; - count2 = last - first; - for ( ; count2 > 0; count2--, vec++ ) - { - FT_Pos x, *px; - - px = vertical ? &vec->x : &vec->y; - x = *px; - - *px = psh_hint_table_tune_coord( table, (FT_Int)x ); - } - } - - first = last; - } - } - else /* no hints in this glyph, simply scale the outline */ - { - FT_Vector* vec; - - vec = outline->points; - count = outline->n_points; - - if ( vertical ) - { - for ( ; count > 0; count--, vec++ ) - vec->x = FT_MulFix( vec->x, scale ) + delta; - } - else - { - for ( ; count > 0; count--, vec++ ) - vec->y = FT_MulFix( vec->y, scale ) + delta; - } - } - } - - - - - - - - - - - FT_LOCAL_DEF FT_Error - ps_hints_apply( PS_Hints ps_hints, - FT_Outline* outline, - PSH_Globals globals ) - { - PSH_Hint_TableRec hints; - FT_Error error; - FT_Int dimension; - - for ( dimension = 1; dimension >= 0; dimension-- ) - { - PS_Dimension dim = &ps_hints->dimension[dimension]; - - /* initialise hints table */ - memset( &hints, 0, sizeof(hints) ); - error = psh_hint_table_init( &hints, - &dim->hints, - &dim->masks, - &dim->counters, - ps_hints->memory ); - if (error) goto Exit; - - psh_hint_table_tune_outline( &hints, - outline, - globals, - dimension ); - - psh_hint_table_done( &hints, ps_hints->memory ); - } - - Exit: - return error; - } \ No newline at end of file diff --git a/src/pshinter/pshfit.h b/src/pshinter/pshfit.h deleted file mode 100644 index 22b85373e..000000000 --- a/src/pshinter/pshfit.h +++ /dev/null @@ -1,110 +0,0 @@ -/***************************************************************************/ -/* */ -/* pshfit.h */ -/* */ -/* Postscript (Type 1/CFF) outline grid-fitter */ -/* */ -/* Copyright 2001 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. */ -/* */ -/* */ -/* process the hints provided by the Postscript hints recorder. This */ -/* means sorting and scaling the hints, calling the "optimiser" to */ -/* process them, then grid-fitting the glyph outline based on the */ -/* optimised hints information. */ -/* */ -/* (the real hinting "intelligence" is in "pshoptim.h", not here) */ -/* */ -/***************************************************************************/ - -#ifndef __PS_HINTER_FITTER_H__ -#define __PS_HINTER_FITTER_H__ - -#include "pshrec.h" - -FT_BEGIN_HEADER - - typedef struct PSH_HintRec_* PSH_Hint; - - typedef enum - { - PSH_HINT_FLAG_GHOST = PS_HINT_FLAG_GHOST, - PSH_HINT_FLAG_BOTTOM = PS_HINT_FLAG_BOTTOM, - PSH_HINT_FLAG_ACTIVE = 4 - - } PSH_Hint_Flags; - -#define psh_hint_is_active(x) (((x)->flags & PSH_HINT_FLAG_ACTIVE) != 0) -#define psh_hint_is_ghost(x) (((x)->flags & PSH_HINT_FLAG_GHOST ) != 0) - -#define psh_hint_activate(x) (x)->flags |= PSH_HINT_FLAG_ACTIVE -#define psh_hint_deactivate(x) (x)->flags &= ~PSH_HINT_FLAG_ACTIVE - - typedef struct PSH_HintRec_ - { - FT_Int org_pos; - FT_Int org_len; - FT_Pos cur_pos; - FT_Pos cur_len; - - FT_UInt flags; - - PSH_Hint parent; - FT_Int order; - - } PSH_HintRec; - - - /* this is an interpolation zone used for strong points */ - /* weak points are interpolated according to their strong */ - /* neighbours.. */ - typedef struct PSH_ZoneRec_ - { - FT_Fixed scale; - FT_Fixed delta; - FT_Pos min; - FT_Pos max; - - } PSH_ZoneRec, *PSH_Zone; - - - typedef struct PSH_Hint_TableRec_ - { - FT_UInt max_hints; - FT_UInt num_hints; - PSH_Hint hints; - PSH_Hint* sort; - PSH_Hint* sort_global; - FT_UInt num_zones; - PSH_Zone zones; - PSH_Zone zone; - PS_Mask_Table hint_masks; - PS_Mask_Table counter_masks; - - } PSH_Hint_TableRec, *PSH_Hint_Table; - - - FT_LOCAL FT_Error - ps_hints_apply( PS_Hints ps_hints, - FT_Outline* outline, - PSH_Globals globals ); - - -#ifdef DEBUG_VIEW - extern int ps_debug_no_horz_hints; - extern int ps_debug_no_vert_hints; - extern PSH_Hint_Table ps_debug_hint_table; - - typedef void (*PSH_HintFunc)( PSH_Hint hint, FT_Bool vertical ); - extern PSH_HintFunc ps_debug_hint_func; -#endif - -FT_END_HEADER - -#endif /* __PS_HINTER_FITTER_H__ */ diff --git a/src/pshinter/pshglob.c b/src/pshinter/pshglob.c index 02c2e14a2..3dfdd2400 100644 --- a/src/pshinter/pshglob.c +++ b/src/pshinter/pshglob.c @@ -3,6 +3,10 @@ #include FT_INTERNAL_OBJECTS_H #include "pshglob.h" +#ifdef DEBUG_HINTER + extern PSH_Globals ps_debug_globals = 0; +#endif + /* "simple" ps hinter globals management, inspired from the new auto-hinter */ /*************************************************************************/ @@ -87,6 +91,7 @@ static void psh_blues_set_zones_0( PSH_Blues target, + FT_Bool is_others, FT_UInt read_count, FT_Short* read, PSH_Blue_Table top_table, @@ -94,26 +99,34 @@ { FT_UInt count_top = top_table->count; FT_UInt count_bot = bot_table->count; + FT_Bool first = 1; for ( ; read_count > 0; read_count -= 2 ) { FT_Int reference, delta; FT_UInt count; PSH_Blue_Zone zones, zone; + FT_Bool top; /* read blue zone entry, and select target top/bottom zone */ - reference = read[0]; - delta = read[1] - reference; - - if ( delta >= 0 ) + top = 0; + if ( first || is_others ) { - zones = top_table->zones; - count = count_top; + reference = read[1]; + delta = read[0] - reference; + + zones = bot_table->zones; + count = count_bot; + first = 0; } else { - zones = bot_table->zones; - count = count_bot; + reference = read[0]; + delta = read[1] - reference; + + zones = top_table->zones; + count = count_top; + top = 1; } /* insert into sorted table */ @@ -149,7 +162,7 @@ zone->org_ref = reference; zone->org_delta = delta; - if ( delta >= 0 ) + if ( top ) count_top ++; else count_bot ++; @@ -195,8 +208,8 @@ bot_table->count = 0; /* first, the blues */ - psh_blues_set_zones_0( target, count, blues, top_table, bot_table ); - psh_blues_set_zones_0( target, count_others, other_blues, top_table, bot_table ); + psh_blues_set_zones_0( target, 0, count, blues, top_table, bot_table ); + psh_blues_set_zones_0( target, 1, count_others, other_blues, top_table, bot_table ); count_top = top_table->count; count_bot = bot_table->count; @@ -206,13 +219,16 @@ { PSH_Blue_Zone zone = top_table->zones; - for ( count = count_top-1; count > 0; count--, zone++ ) + for ( count = count_top; count > 0; count--, zone++ ) { FT_Int delta; - - delta = zone[1].org_ref - zone[0].org_ref; - if ( zone->org_delta > delta ) - zone->org_delta = delta; + + if ( count > 1 ) + { + delta = zone[1].org_ref - zone[0].org_ref; + if ( zone->org_delta > delta ) + zone->org_delta = delta; + } zone->org_bottom = zone->org_ref; zone->org_top = zone->org_delta + zone->org_ref; @@ -224,13 +240,16 @@ { PSH_Blue_Zone zone = bot_table->zones; - for ( count = count_bot-1; count > 0; count--, zone++ ) + for ( count = count_bot; count > 0; count--, zone++ ) { FT_Int delta; - delta = zone[0].org_ref - zone[1].org_ref; - if ( zone->org_delta < delta ) - zone->org_delta = delta; + if ( count > 1 ) + { + delta = zone[0].org_ref - zone[1].org_ref; + if ( zone->org_delta < delta ) + zone->org_delta = delta; + } zone->org_top = zone->org_ref; zone->org_bottom = zone->org_delta + zone->org_ref; @@ -256,7 +275,7 @@ /* checking that the interval is smaller than the fuzz */ top = zone->org_top; - for ( count--; count > 0; count--, zone++ ) + for ( count--; count > 0; count-- ) { bot = zone[1].org_bottom; delta = bot - top; @@ -318,6 +337,13 @@ /* round scaled reference position */ zone->cur_ref = ( zone->cur_ref + 32 ) & -64; + +#if 0 + if ( zone->cur_ref > zone->cur_top ) + zone->cur_ref -= 64; + else if ( zone->cur_ref < zone->cur_bottom ) + zone->cur_ref += 64; +#endif } } @@ -326,10 +352,10 @@ FT_LOCAL_DEF void - psh_blues_snap_stem( PSH_Blues blues, - FT_Int stem_top, - FT_Int stem_bot, - PSH_Blue_Alignement alignment ) + psh_blues_snap_stem( PSH_Blues blues, + FT_Int stem_top, + FT_Int stem_bot, + PSH_Alignment alignment ) { PSH_Blue_Table table; FT_UInt count; @@ -341,7 +367,7 @@ table = &blues->normal_top; count = table->count; zone = table->zones; - for ( ; count > 0; count-- ) + for ( ; count > 0; count--, zone++ ) { if ( stem_top < zone->org_bottom ) break; @@ -358,7 +384,7 @@ table = &blues->normal_bottom; count = table->count; zone = table->zones; - for ( ; count > 0; count-- ) + for ( ; count > 0; count--, zone++ ) { if ( stem_bot < zone->org_bottom ) break; @@ -367,6 +393,7 @@ { alignment->align |= PSH_BLUE_ALIGN_BOT; alignment->align_bot = zone->cur_ref; + break; } } } @@ -397,6 +424,10 @@ globals->blues.family_bottom.count = 0; FREE( globals ); + +#ifdef DEBUG_HINTER + ps_debug_globals = 0; +#endif } } @@ -467,6 +498,10 @@ globals->dimension[0].scale_delta = 0; globals->dimension[1].scale_mult = 0; globals->dimension[1].scale_delta = 0; + +#ifdef DEBUG_HINTER + ps_debug_globals = globals; +#endif } *aglobals = globals; diff --git a/src/pshinter/pshglob.h b/src/pshinter/pshglob.h index 6fdda05b0..dae74b471 100644 --- a/src/pshinter/pshglob.h +++ b/src/pshinter/pshglob.h @@ -141,7 +141,7 @@ FT_BEGIN_HEADER FT_Pos align_top; FT_Pos align_bot; - } PSH_Blue_AlignementRec, *PSH_Blue_Alignement; + } PSH_AlignmentRec, *PSH_Alignment; FT_LOCAL void @@ -156,12 +156,15 @@ FT_BEGIN_HEADER /* snap a stem to one or two blue zones */ FT_LOCAL void - psh_blues_snap_stem( PSH_Blues blues, - FT_Int stem_top, - FT_Int stem_bot, - PSH_Blue_Alignement alignment ); + psh_blues_snap_stem( PSH_Blues blues, + FT_Int stem_top, + FT_Int stem_bot, + PSH_Alignment alignment ); /* */ - + +#ifdef DEBUG_HINTER + extern PSH_Globals ps_debug_globals; +#endif FT_END_HEADER diff --git a/src/pshinter/pshinter.c b/src/pshinter/pshinter.c index 592cda3f9..07c63303c 100644 --- a/src/pshinter/pshinter.c +++ b/src/pshinter/pshinter.c @@ -21,8 +21,9 @@ #include #include "pshrec.c" #include "pshglob.c" -#include "pshfit.c" +#include "pshalgo1.c" +#include "pshalgo2.c" #include "pshmod.c" -#include "pshoptim.c" + /* END */ diff --git a/src/pshinter/pshmod.c b/src/pshinter/pshmod.c index 2838400e7..65b360d07 100644 --- a/src/pshinter/pshmod.c +++ b/src/pshinter/pshmod.c @@ -18,6 +18,7 @@ #include #include FT_INTERNAL_OBJECTS_H #include "pshrec.h" +#include "pshalgo.h" /* the Postscript Hinter module structure */ typedef struct diff --git a/src/pshinter/pshoptim.c b/src/pshinter/pshoptim.c deleted file mode 100644 index eef2f846f..000000000 --- a/src/pshinter/pshoptim.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "pshoptim.h" - - -#ifdef DEBUG_VIEW - void - ps_simple_scale( PSH_Hint_Table table, - FT_Fixed scale, - FT_Fixed delta, - FT_Bool vertical ) - { - PSH_Hint hint; - FT_UInt count; - - for ( count = 0; count < table->num_hints; count++ ) - { - hint = table->sort[count]; - if ( psh_hint_is_active(hint) ) - { - hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; - hint->cur_len = FT_MulFix( hint->org_len, scale ); - - if (ps_debug_hint_func) - ps_debug_hint_func( hint, vertical ); - } - } - } -#endif - - FT_LOCAL_DEF FT_Error - psh_hint_table_optimize( PSH_Hint_Table table, - PSH_Globals globals, - FT_Outline* outline, - FT_Bool vertical ) - { - PSH_Dimension dim = &globals->dimension[vertical]; - FT_Fixed scale = dim->scale_mult; - FT_Fixed delta = dim->scale_delta; - -#ifdef DEBUG_VIEW - if ( ps_debug_no_vert_hints && vertical ) - { - ps_simple_scale( table, scale, delta, vertical ); - return 0; - } - - if ( ps_debug_no_horz_hints && !vertical ) - { - ps_simple_scale( table, scale, delta, vertical ); - return 0; - } -#endif - - /* XXXX: for now, we only scale the hints to test all other aspects */ - /* of the Postscript Hinter.. */ - { - PSH_Hint hint; - FT_UInt count; - - for ( count = 0; count < table->num_hints; count++ ) - { - hint = table->sort[count]; - if ( psh_hint_is_active(hint) ) - { -# if 1 - FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; - FT_Pos len = FT_MulFix( hint->org_len, scale ); - - FT_Pos fit_center; - FT_Pos fit_len; - - PSH_Blue_AlignementRec align; - - /* compute fitted width/height */ - fit_len = psh_dimension_snap_width( dim, hint->org_len ); - if ( fit_len < 64 ) - fit_len = 64; - else - fit_len = (fit_len + 16 ) & -64; - - hint->cur_len = fit_len; - - /* check blue zones for horizontal stems */ - align.align = 0; - if (!vertical) - { - psh_blues_snap_stem( &globals->blues, - hint->org_pos + hint->org_len, - hint->org_pos, - &align ); - } - - switch (align.align) - { - case PSH_BLUE_ALIGN_TOP: - { - /* the top of the stem is aligned against a blue zone */ - hint->cur_pos = align.align_top - fit_len; - break; - } - - case PSH_BLUE_ALIGN_BOT: - { - /* the bottom of the stem is aligned against a blue zone */ - hint->cur_pos = align.align_bot; - break; - } - - case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: - { - /* both edges of the stem are aligned against blue zones */ - hint->cur_pos = align.align_bot; - hint->cur_len = align.align_top - align.align_bot; - } - break; - - default: - /* normal processing */ - if ( (fit_len/64) & 1 ) - { - /* odd number of pixels */ - fit_center = ((pos + (len >> 1)) & -64) + 32; - } - else - { - /* even number of pixels */ - fit_center = (pos + (len >> 1) + 32) & -64; - } - - hint->cur_pos = fit_center - (fit_len >> 1); - } -# else - hint->cur_pos = (FT_MulFix( hint->org_pos, scale ) + delta + 32) & -64; - hint->cur_len = FT_MulFix( hint->org_len, scale ); -# endif - -#ifdef DEBUG_VIEW - if (ps_debug_hint_func) - ps_debug_hint_func( hint, vertical ); -#endif - } - } - } - - return 0; - } diff --git a/src/pshinter/pshoptim.h b/src/pshinter/pshoptim.h deleted file mode 100644 index eb1605e87..000000000 --- a/src/pshinter/pshoptim.h +++ /dev/null @@ -1,41 +0,0 @@ -/***************************************************************************/ -/* */ -/* pshoptim.h */ -/* */ -/* Postscript Hints optimiser and grid-fitter */ -/* */ -/* Copyright 2001 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. */ -/* */ -/* */ -/* process the hints provided by the fitter in order to optimize them */ -/* for the pixel grid, depending on the current glyph outline and */ -/* globals. This is where most of the PS Hinter's intelligence is */ -/* */ -/* XXXX: Until now, only a dummy implementation is provided in order */ -/* to test all other functions of the Postscript Hinter. */ -/* */ -/***************************************************************************/ - -#ifndef __PS_HINTER_OPTIMISER_H__ -#define __PS_HINTER_OPTIMISER_H__ - -#include "pshfit.h" - -FT_BEGIN_HEADER - - FT_LOCAL FT_Error - psh_hint_table_optimize( PSH_Hint_Table table, - PSH_Globals globals, - FT_Outline* outline, - FT_Bool vertical ); - -FT_END_HEADER - -#endif /* __PS_HINTER_OPTIMISER_H__ */ diff --git a/src/pshinter/pshrec.c b/src/pshinter/pshrec.c index 5c63f68c2..8eb6810f7 100644 --- a/src/pshinter/pshrec.c +++ b/src/pshinter/pshrec.c @@ -3,7 +3,15 @@ #include FT_INTERNAL_OBJECTS_H #include FT_INTERNAL_DEBUG_H #include "pshrec.h" -#include "pshfit.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 + /***********************************************************************/ /***********************************************************************/ @@ -1041,7 +1049,7 @@ 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; + funcs->apply = (T1_Hints_ApplyFunc) PS_HINTS_APPLY_FUNC; } @@ -1097,7 +1105,7 @@ 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; + funcs->apply = (T2_Hints_ApplyFunc) PS_HINTS_APPLY_FUNC; } diff --git a/src/pshinter/pshrec.h b/src/pshinter/pshrec.h index fbf7ddbb2..cdcf05777 100644 --- a/src/pshinter/pshrec.h +++ b/src/pshinter/pshrec.h @@ -151,10 +151,11 @@ FT_BEGIN_HEADER FT_LOCAL void t2_hints_funcs_init( T2_Hints_FuncsRec* funcs ); -#define xxxDEBUG_VIEW -#ifdef DEBUG_VIEW - extern PS_Hints the_ps_hints; +#ifdef DEBUG_HINTER + extern PS_Hints ps_debug_hints; + extern int ps_debug_no_horz_hints; + extern int ps_debug_no_vert_hints; #endif /* */ diff --git a/src/smooth/ftgrays.c b/src/smooth/ftgrays.c index a12a9ecfb..9de014176 100644 --- a/src/smooth/ftgrays.c +++ b/src/smooth/ftgrays.c @@ -82,7 +82,7 @@ #include /* for memcpy() */ - +#include /*************************************************************************/ /* */ @@ -94,6 +94,8 @@ #define FT_COMPONENT trace_aaraster +#define ErrRaster_MemoryOverflow -4 + #ifdef _STANDALONE_ @@ -264,12 +266,13 @@ void* render_span_data; int span_y; - int band_size; - int band_shoot; - int conic_level; - int cubic_level; + int band_size; + int band_shoot; + int conic_level; + int cubic_level; - void* memory; + void* memory; + jmp_buf jump_buffer; } TRaster, *PRaster; @@ -296,10 +299,11 @@ /* Compute the outline bounding box. */ /* */ static - void compute_cbox( RAS_ARG_ FT_Outline* outline ) + void compute_cbox( RAS_ARG ) { - FT_Vector* vec = outline->points; - FT_Vector* limit = vec + outline->n_points; + FT_Outline* outline = &ras.outline; + FT_Vector* vec = outline->points; + FT_Vector* limit = vec + outline->n_points; if ( outline->n_points <= 0 ) @@ -339,7 +343,7 @@ /* Record the current cell in the table. */ /* */ static - int record_cell( RAS_ARG ) + void record_cell( RAS_ARG ) { PCell cell; @@ -347,7 +351,7 @@ if ( !ras.invalid && ( ras.area | ras.cover ) ) { if ( ras.num_cells >= ras.max_cells ) - return 1; + longjmp( ras.jump_buffer, 1 ); cell = ras.cells + ras.num_cells++; cell->x = ras.ex - ras.min_ex; @@ -355,8 +359,6 @@ cell->area = ras.area; cell->cover = ras.cover; } - - return 0; } @@ -365,8 +367,8 @@ /* Set the current cell to a new position. */ /* */ static - int set_cell( RAS_ARG_ TScan ex, - TScan ey ) + void set_cell( RAS_ARG_ TScan ex, + TScan ey ) { int invalid, record, clean; @@ -402,8 +404,8 @@ /* record the previous cell if needed (i.e., if we changed the cell */ /* position, of changed the `invalid' flag) */ - if ( ( ras.invalid != invalid || record ) && record_cell( RAS_VAR ) ) - return 1; + if ( ras.invalid != invalid || record ) + record_cell( RAS_VAR ); if ( clean ) { @@ -414,7 +416,6 @@ ras.invalid = invalid; ras.ex = ex; ras.ey = ey; - return 0; } @@ -436,7 +437,7 @@ ras.last_ey = SUBPIXELS( ey ); ras.invalid = 0; - (void)set_cell( RAS_VAR_ ex, ey ); + set_cell( RAS_VAR_ ex, ey ); } @@ -445,11 +446,11 @@ /* Render a scanline as one or more cells. */ /* */ static - int render_scanline( RAS_ARG_ TScan ey, - TPos x1, - TScan y1, - TPos x2, - TScan y2 ) + void render_scanline( RAS_ARG_ TScan ey, + TPos x1, + TScan y1, + TPos x2, + TScan y2 ) { TScan ex1, ex2, fx1, fx2, delta; long p, first, dx; @@ -465,7 +466,10 @@ /* trivial case. Happens often */ if ( y1 == y2 ) - return set_cell( RAS_VAR_ ex2, ey ); + { + set_cell( RAS_VAR_ ex2, ey ); + return; + } /* everything is located in a single cell. That is easy! */ /* */ @@ -474,7 +478,7 @@ delta = y2 - y1; ras.area += ( fx1 + fx2 ) * delta; ras.cover += delta; - return 0; + return; } /* ok, we'll have to render a run of adjacent cells on the same */ @@ -504,8 +508,7 @@ ras.cover += delta; ex1 += incr; - if ( set_cell( RAS_VAR_ ex1, ey ) ) - goto Error; + set_cell( RAS_VAR_ ex1, ey ); y1 += delta; if ( ex1 != ex2 ) @@ -535,19 +538,13 @@ ras.cover += delta; y1 += delta; ex1 += incr; - if ( set_cell( RAS_VAR_ ex1, ey ) ) - goto Error; + set_cell( RAS_VAR_ ex1, ey ); } } delta = y2 - y1; ras.area += ( fx2 + ONE_PIXEL - first ) * delta; ras.cover += delta; - - return 0; - - Error: - return 1; } @@ -556,7 +553,7 @@ /* Render a given line as a series of scanlines. */ /* */ static - int render_line( RAS_ARG_ TPos to_x, + void render_line( RAS_ARG_ TPos to_x, TPos to_y ) { TScan ey1, ey2, fy1, fy2; @@ -594,8 +591,7 @@ /* everything is on a single scanline */ if ( ey1 == ey2 ) { - if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ) ) - goto Error; + render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ); goto End; } @@ -621,12 +617,10 @@ } x = ras.x + delta; - if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, first ) ) - goto Error; + render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, first ); ey1 += incr; - if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) ) - goto Error; + set_cell( RAS_VAR_ TRUNC( x ), ey1 ); if ( ey1 != ey2 ) { @@ -651,29 +645,20 @@ } x2 = x + delta; - if ( render_scanline( RAS_VAR_ ey1, - x, ONE_PIXEL - first, x2, first ) ) - goto Error; + render_scanline( RAS_VAR_ ey1, x, ONE_PIXEL - first, x2, first ); x = x2; + ey1 += incr; - if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) ) - goto Error; + set_cell( RAS_VAR_ TRUNC( x ), ey1 ); } } - if ( render_scanline( RAS_VAR_ ey1, - x, ONE_PIXEL - first, to_x, fy2 ) ) - goto Error; + render_scanline( RAS_VAR_ ey1, x, ONE_PIXEL - first, to_x, fy2 ); End: ras.x = to_x; ras.y = to_y; ras.last_ey = SUBPIXELS( ey2 ); - - return 0; - - Error: - return 1; } @@ -698,7 +683,7 @@ static - int render_conic( RAS_ARG_ FT_Vector* control, + void render_conic( RAS_ARG_ FT_Vector* control, FT_Vector* to ) { TPos dx, dy; @@ -737,8 +722,9 @@ mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4; mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4; - return render_line( RAS_VAR_ mid_x, mid_y ) || - render_line( RAS_VAR_ to_x, to_y ); + render_line( RAS_VAR_ mid_x, mid_y ); + render_line( RAS_VAR_ to_x, to_y ); + return; } arc = ras.bez_stack; @@ -792,15 +778,14 @@ mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4; mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4; - if ( render_line( RAS_VAR_ mid_x, mid_y ) || - render_line( RAS_VAR_ to_x, to_y ) ) - return 1; + render_line( RAS_VAR_ mid_x, mid_y ); + render_line( RAS_VAR_ to_x, to_y ); top--; arc -= 2; } } - return 0; + return; } @@ -833,7 +818,7 @@ static - int render_cubic( RAS_ARG_ FT_Vector* control1, + void render_cubic( RAS_ARG_ FT_Vector* control1, FT_Vector* control2, FT_Vector* to ) { @@ -885,8 +870,9 @@ mid_y = ( ras.y + to_y + 3 * UPSCALE( control1->y + control2->y ) ) / 8; - return render_line( RAS_VAR_ mid_x, mid_y ) || - render_line( RAS_VAR_ to_x, to_y ); + render_line( RAS_VAR_ mid_x, mid_y ); + render_line( RAS_VAR_ to_x, to_y ); + return; } arc = ras.bez_stack; @@ -941,14 +927,13 @@ mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8; mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8; - if ( render_line( RAS_VAR_ mid_x, mid_y ) || - render_line( RAS_VAR_ to_x, to_y ) ) - return 1; + render_line( RAS_VAR_ mid_x, mid_y ); + render_line( RAS_VAR_ to_x, to_y ); top --; arc -= 3; } } - return 0; + return; } @@ -1157,7 +1142,9 @@ /* start to a new position */ x = UPSCALE( to->x ); y = UPSCALE( to->y ); + start_cell( (PRaster)raster, TRUNC( x ), TRUNC( y ) ); + ((PRaster)raster)->x = x; ((PRaster)raster)->y = y; return 0; @@ -1168,8 +1155,9 @@ int Line_To( FT_Vector* to, FT_Raster raster ) { - return render_line( (PRaster)raster, - UPSCALE( to->x ), UPSCALE( to->y ) ); + render_line( (PRaster)raster, + UPSCALE( to->x ), UPSCALE( to->y ) ); + return 0; } @@ -1178,7 +1166,8 @@ FT_Vector* to, FT_Raster raster ) { - return render_conic( (PRaster)raster, control, to ); + render_conic( (PRaster)raster, control, to ); + return 0; } @@ -1188,7 +1177,8 @@ FT_Vector* to, FT_Raster raster ) { - return render_cubic( (PRaster)raster, control1, control2, to ); + render_cubic( (PRaster)raster, control1, control2, to ); + return 0; } @@ -1487,7 +1477,11 @@ void* user ) { #undef SCALED -#define SCALED( x ) ( ( (x) << shift ) - delta ) +#if 0 +# define SCALED( x ) ( ( (x) << shift ) - delta ) +#else +# define SCALED( x) (x) +#endif FT_Vector v_last; FT_Vector v_control; @@ -1502,8 +1496,10 @@ int error; char tag; /* current point's state */ +#if 0 int shift = interface->shift; FT_Pos delta = interface->delta; +#endif first = 0; @@ -1691,8 +1687,8 @@ } TBand; - static - int grays_convert_glyph( RAS_ARG_ FT_Outline* outline ) + static int + grays_convert_glyph_inner( RAS_ARG ) { static const FT_Outline_Funcs interface = @@ -1705,6 +1701,25 @@ 0 }; + volatile int error = 0; + + if ( setjmp( ras.jump_buffer ) == 0 ) + { + error = FT_Outline_Decompose( &ras.outline, &interface, &ras ); + record_cell( RAS_VAR ); + } + else + { + error = ErrRaster_MemoryOverflow; + } + + return error; + } + + + static + int grays_convert_glyph( RAS_ARG_ FT_Outline* outline ) + { TBand bands[40], *band; int n, num_bands; TPos min, max, max_y; @@ -1712,7 +1727,7 @@ /* Set up state in the raster object */ - compute_cbox( RAS_VAR_ outline ); + compute_cbox( RAS_VAR ); /* clip to target bitmap, exit if nothing to do */ clip = &ras.clip_box; @@ -1776,8 +1791,12 @@ ras.min_ey = band->min; ras.max_ey = band->max; +#if 1 + error = grays_convert_glyph_inner( RAS_VAR ); +#else error = FT_Outline_Decompose( outline, &interface, &ras ) || record_cell( RAS_VAR ); +#endif if ( !error ) { @@ -1796,6 +1815,8 @@ band--; continue; } + else if ( error != ErrRaster_MemoryOverflow ) + return 1; /* render pool overflow, we will reduce the render band by half */ bottom = band->min; diff --git a/tests/Jamfile b/tests/Jamfile index 74d3aef23..06625a93e 100644 --- a/tests/Jamfile +++ b/tests/Jamfile @@ -16,7 +16,7 @@ NIRVANA_LINKLIBS = $(NV_TOP)\\objs\\nirvana$(SUFLIB) ; Main $(t) : $(t).c ; LinkLibraries $(t) : $(FT2_LIB) ; - + if $(TOOLSET) = MINGW { LINKKLIBS on $(t)$(SUFEXE) = "-luser32 -lgdi32" ; diff --git a/tests/gview.c b/tests/gview.c index b894aa207..35d236911 100644 --- a/tests/gview.c +++ b/tests/gview.c @@ -7,7 +7,17 @@ /* include FreeType internals to debug hints */ #include <../src/pshinter/pshrec.h> -#include <../src/pshinter/pshfit.h> +#include <../src/pshinter/pshalgo1.h> +#include <../src/pshinter/pshalgo2.h> + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** ROOT DEFINITIONS *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + #include /* for clock() */ @@ -18,7 +28,7 @@ #define CLOCKS_PER_SEC HZ #endif -static int depth = 0; +static int first_glyph = 0; static NV_Renderer renderer; static NV_Painter painter; @@ -42,12 +52,23 @@ static NV_Scale grid_scale = 1.0; static int glyph_index; static int pixel_size = 12; + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** OPTIONS, COLORS and OTHERS *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + static int option_show_axis = 1; static int option_show_dots = 1; -static int option_show_stroke = 1; +static int option_show_stroke = 0; static int option_show_glyph = 1; static int option_show_grid = 1; static int option_show_em = 0; +static int option_show_smooth = 1; +static int option_show_blues = 0; static int option_show_ps_hints = 1; static int option_show_horz_hints = 1; @@ -58,18 +79,18 @@ static int option_hinting = 1; static char temp_message[1024]; -PS_Hints the_ps_hints = 0; -int ps_debug_no_horz_hints = 0; -int ps_debug_no_vert_hints = 0; -PSH_HintFunc ps_debug_hint_func = 0; - #define AXIS_COLOR 0xFFFF0000 #define GRID_COLOR 0xFFD0D0D0 #define ON_COLOR 0xFFFF2000 #define OFF_COLOR 0xFFFF0080 +#define STRONG_COLOR 0xFF404040 +#define INTERP_COLOR 0xFF206040 +#define SMOOTH_COLOR 0xF000B040 #define BACKGROUND_COLOR 0xFFFFFFFF #define TEXT_COLOR 0xFF000000 #define EM_COLOR 0x80008000 +#define BLUES_TOP_COLOR 0x4000008F +#define BLUES_BOT_COLOR 0x40008F00 #define GHOST_HINT_COLOR 0xE00000FF #define STEM_HINT_COLOR 0xE02020FF @@ -84,6 +105,13 @@ Panic( const char* message ) } + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** COMMON GRID DRAWING ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ static void reset_scale( NV_Scale scale ) @@ -188,11 +216,114 @@ draw_grid( void ) } + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** POSTSCRIPT GLOBALS ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#include <../src/pshinter/pshglob.h> + +static void +draw_ps_blue_zones( void ) +{ + if ( option_show_blues && ps_debug_globals ) + { + PSH_Blues blues = &ps_debug_globals->blues; + PSH_Blue_Table table; + NV_Vector v; + FT_Int y1, y2; + FT_UInt count; + PSH_Blue_Zone zone; + + /* draw top zones */ + table = &blues->normal_top; + count = table->count; + zone = table->zones; + + for ( ; count > 0; count--, zone++ ) + { + v.x = 0; + if ( !ps_debug_no_horz_hints ) + { + v.y = zone->cur_ref + zone->cur_delta; + nv_vector_transform( &v, &size_transform ); + } + else + { + v.y = zone->org_ref + zone->org_delta; + nv_vector_transform( &v, &glyph_transform ); + } + y1 = (int)(v.y + 0.5); + + v.x = 0; + if ( !ps_debug_no_horz_hints ) + { + v.y = zone->cur_ref; + nv_vector_transform( &v, &size_transform ); + } + else + { + v.y = zone->org_ref; + nv_vector_transform( &v, &glyph_transform ); + } + y2 = (int)(v.y + 0.5); + + nv_pixmap_fill_rect( target, 0, y1, + target->width, y2-y1+1, + BLUES_TOP_COLOR ); + +#if 0 + printf( "top [%.3f %.3f]\n", zone->cur_bottom/64.0, zone->cur_top/64.0 ); +#endif + } + + + /* draw bottom zones */ + table = &blues->normal_bottom; + count = table->count; + zone = table->zones; + + for ( ; count > 0; count--, zone++ ) + { + v.x = 0; + v.y = zone->cur_ref; + nv_vector_transform( &v, &size_transform ); + y1 = (int)(v.y + 0.5); + + v.x = 0; + v.y = zone->cur_ref + zone->cur_delta; + nv_vector_transform( &v, &size_transform ); + y2 = (int)(v.y + 0.5); + + nv_pixmap_fill_rect( target, 0, y1, + target->width, y2-y1+1, + BLUES_BOT_COLOR ); + +#if 0 + printf( "bot [%.3f %.3f]\n", zone->cur_bottom/64.0, zone->cur_top/64.0 ); +#endif + } + } +} + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** POSTSCRIPT HINTER ALGORITHM 1 ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#include <../src/pshinter/pshalgo1.h> + static int pshint_cpos = 0; static int pshint_vertical = -1; static void -draw_ps_hint( PSH_Hint hint, FT_Bool vertical ) +draw_ps1_hint( PSH1_Hint hint, FT_Bool vertical ) { int x1, x2; NV_Vector v; @@ -224,17 +355,17 @@ draw_ps_hint( PSH_Hint hint, FT_Bool vertical ) x2 = (int)(v.x + 0.5); nv_pixmap_fill_rect( target, x1, 0, 1, target->height, - psh_hint_is_ghost(hint) + psh1_hint_is_ghost(hint) ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); - if ( psh_hint_is_ghost(hint) ) + if ( psh1_hint_is_ghost(hint) ) { x1 --; x2 = x1 + 2; } else nv_pixmap_fill_rect( target, x2, 0, 1, target->height, - psh_hint_is_ghost(hint) + psh1_hint_is_ghost(hint) ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); nv_pixmap_fill_rect( target, x1, pshint_cpos, x2+1-x1, 1, @@ -256,37 +387,208 @@ draw_ps_hint( PSH_Hint hint, FT_Bool vertical ) x2 = (int)(v.y + 0.5); nv_pixmap_fill_rect( target, 0, x1, target->width, 1, - psh_hint_is_ghost(hint) + psh1_hint_is_ghost(hint) ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); - if ( psh_hint_is_ghost(hint) ) + if ( psh1_hint_is_ghost(hint) ) { x1 --; x2 = x1 + 2; } else nv_pixmap_fill_rect( target, 0, x2, target->width, 1, - psh_hint_is_ghost(hint) + psh1_hint_is_ghost(hint) ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); nv_pixmap_fill_rect( target, pshint_cpos, x2, 1, x1+1-x2, STEM_JOIN_COLOR ); } +#if 0 printf( "[%7.3f %7.3f] %c\n", hint->cur_pos/64.0, (hint->cur_pos+hint->cur_len)/64.0, vertical ? 'v' : 'h' ); +#endif pshint_cpos += 10; } + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** POSTSCRIPT HINTER ALGORITHM 2 ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#include <../src/pshinter/pshalgo2.h> + +static void +draw_ps2_hint( PSH2_Hint hint, FT_Bool vertical ) +{ + int x1, x2; + NV_Vector v; + + if ( pshint_vertical != vertical ) + { + if (vertical) + pshint_cpos = 40; + else + pshint_cpos = 10; + + pshint_vertical = vertical; + } + + if (vertical) + { + if ( !option_show_vert_hints ) + return; + + v.x = hint->cur_pos; + v.y = 0; + nv_vector_transform( &v, &size_transform ); + x1 = (int)(v.x + 0.5); + + v.x = hint->cur_pos + hint->cur_len; + v.y = 0; + nv_vector_transform( &v, &size_transform ); + x2 = (int)(v.x + 0.5); + + nv_pixmap_fill_rect( target, x1, 0, 1, target->height, + psh2_hint_is_ghost(hint) + ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); + + if ( psh2_hint_is_ghost(hint) ) + { + x1 --; + x2 = x1 + 2; + } + else + nv_pixmap_fill_rect( target, x2, 0, 1, target->height, + psh2_hint_is_ghost(hint) + ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); + + nv_pixmap_fill_rect( target, x1, pshint_cpos, x2+1-x1, 1, + STEM_JOIN_COLOR ); + } + else + { + if (!option_show_horz_hints) + return; + + v.y = hint->cur_pos; + v.x = 0; + nv_vector_transform( &v, &size_transform ); + x1 = (int)(v.y + 0.5); + + v.y = hint->cur_pos + hint->cur_len; + v.x = 0; + nv_vector_transform( &v, &size_transform ); + x2 = (int)(v.y + 0.5); + + nv_pixmap_fill_rect( target, 0, x1, target->width, 1, + psh2_hint_is_ghost(hint) + ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); + + if ( psh2_hint_is_ghost(hint) ) + { + x1 --; + x2 = x1 + 2; + } + else + nv_pixmap_fill_rect( target, 0, x2, target->width, 1, + psh2_hint_is_ghost(hint) + ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); + + nv_pixmap_fill_rect( target, pshint_cpos, x2, 1, x1+1-x2, + STEM_JOIN_COLOR ); + } + +#if 0 + printf( "[%7.3f %7.3f] %c\n", hint->cur_pos/64.0, (hint->cur_pos+hint->cur_len)/64.0, vertical ? 'v' : 'h' ); +#endif + + pshint_cpos += 10; +} + + +static void +ps2_draw_control_points( void ) +{ + if ( ps2_debug_glyph ) + { + PSH2_Glyph glyph = ps2_debug_glyph; + PSH2_Point point = glyph->points; + FT_UInt count = glyph->num_points; + NV_Transform transform, *trans = &transform; + NV_Path vert_rect; + NV_Path horz_rect; + NV_Path dot, circle; + + nv_path_new_rectangle( renderer, -1, -6, 2, 12, 0, 0, &vert_rect ); + nv_path_new_rectangle( renderer, -6, -1, 12, 2, 0, 0, &horz_rect ); + nv_path_new_circle( renderer, 0, 0, 3., &dot ); + nv_path_stroke( dot, 0.6, nv_path_linecap_butt, nv_path_linejoin_miter, 1., &circle ); + + for ( ; count > 0; count--, point++ ) + { + NV_Vector vec; + + vec.x = point->cur_x; + vec.y = point->cur_y; + nv_vector_transform( &vec, &size_transform ); + + nv_transform_set_translate( trans, vec.x, vec.y ); + + if ( option_show_smooth && !psh2_point_is_smooth(point) ) + { + nv_painter_set_color( painter, SMOOTH_COLOR, 256 ); + nv_painter_fill_path( painter, trans, 0, circle ); + } + + if (option_show_horz_hints) + { + if ( point->flags_y & PSH2_POINT_STRONG ) + { + nv_painter_set_color( painter, STRONG_COLOR, 256 ); + nv_painter_fill_path( painter, trans, 0, horz_rect ); + } + } + + if (option_show_vert_hints) + { + if ( point->flags_x & PSH2_POINT_STRONG ) + { + nv_painter_set_color( painter, STRONG_COLOR, 256 ); + nv_painter_fill_path( painter, trans, 0, vert_rect ); + } + } + } + + nv_path_destroy( circle ); + nv_path_destroy( dot ); + nv_path_destroy( horz_rect ); + nv_path_destroy( vert_rect ); + } +} + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** MAIN LOOP(S) *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + static void draw_glyph( int glyph_index ) { NV_Path path; pshint_vertical = -1; - ps_debug_hint_func = option_show_ps_hints ? draw_ps_hint : 0; + + ps1_debug_hint_func = option_show_ps_hints ? draw_ps1_hint : 0; + ps2_debug_hint_func = option_show_ps_hints ? draw_ps2_hint : 0; error = FT_Load_Glyph( face, glyph_index, option_hinting ? FT_LOAD_NO_BITMAP @@ -334,6 +636,7 @@ draw_glyph( int glyph_index ) NV_Int n, first, last; nv_path_new_circle( renderer, 0, 0, 2., &plot ); + nv_path_get_outline( path, NULL, memory, &out ); first = 0; @@ -355,8 +658,10 @@ draw_glyph( int glyph_index ) vec = out.points + m; nv_transform_set_translate( &trans, vec->x/64.0, vec->y/64.0 ); + nv_painter_set_color( painter, color, 256 ); nv_painter_fill_path( painter, &trans, 0, plot ); + } first = last + 1; @@ -390,105 +695,25 @@ draw_glyph( int glyph_index ) } -#if 0 -static void -draw_ps_hints( void ) -{ - if ( option_show_ps_hints && the_ps_hints ) - { - PS_Dimension dim; - PS_Hint hint; - NV_UInt count; - NV_Int cpos; - NV_Vector v; - - /* draw vertical stems */ - if ( option_show_vert_hints ) - { - dim = &the_ps_hints->dimension[1]; - hint = dim->hints.hints; - cpos = 40; - for ( count = dim->hints.num_hints; count > 0; count--, hint++ ) - { - NV_Int x1, x2; - - v.x = hint->pos; - v.y = 0; - nv_vector_transform( &v, &glyph_transform ); - x1 = (int)(v.x+0.5); - x1 = glyph_org_x + hint->pos*glyph_scale; - nv_pixmap_fill_rect( target, x1, 0, 1, target->height, - ps_hint_is_ghost(hint) - ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); - - if ( !ps_hint_is_ghost(hint) ) - { - v.x = hint->pos + hint->len; - v.y = 0; - nv_vector_transform( &v, &glyph_transform ); - x2 = (int)(v.x+0.5); - x2 = glyph_org_x + (hint->pos + hint->len)*glyph_scale; - nv_pixmap_fill_rect( target, x2, 0, 1, target->height, - STEM_HINT_COLOR ); - } - else - { - x1 -= 1; - x2 = x1 + 2; - } - - nv_pixmap_fill_rect( target, x1, cpos, x2-x1+1, 1, - STEM_JOIN_COLOR ); - cpos += 10; - } - } +#define TOGGLE_OPTION(var,prefix) \ + { \ + var = !var; \ + sprintf( temp_message, prefix " is now %s", \ + var ? "on" : "off" ); \ + break; \ + } - /* draw horizontal stems */ - if ( option_show_horz_hints ) - { - dim = &the_ps_hints->dimension[0]; - hint = dim->hints.hints; - cpos = 10; - for ( count = dim->hints.num_hints; count > 0; count--, hint++ ) - { - NV_Int y1, y2; - - v.x = 0; - v.y = hint->pos; - nv_vector_transform( &v, &glyph_transform ); - y1 = (int)(v.y+0.5); - y1 = glyph_org_y - hint->pos*glyph_scale; - nv_pixmap_fill_rect( target, 0, y1, target->width, 1, - ps_hint_is_ghost(hint) - ? GHOST_HINT_COLOR : STEM_HINT_COLOR ); - - if ( !ps_hint_is_ghost(hint) ) - { - v.x = 0; - v.y = hint->pos + hint->len; - nv_vector_transform( &v, &glyph_transform ); - y2 = (int)(v.y+0.5); - y2 = glyph_org_y - (hint->pos + hint->len)*glyph_scale; - nv_pixmap_fill_rect( target, 0, y2, target->width, 1, - STEM_HINT_COLOR ); - } - else - { - y1 -= 1; - y2 = y1 + 2; - } - - nv_pixmap_fill_rect( target, cpos, y2, 1, y1-y2+1, - STEM_JOIN_COLOR ); - cpos += 10; - } - } - } -} -#endif +#define TOGGLE_OPTION_NEG(var,prefix) \ + { \ + var = !var; \ + sprintf( temp_message, prefix " is now %s", \ + !var ? "on" : "off" ); \ + break; \ + } + static void handle_event( NVV_EventRec* ev ) { @@ -509,34 +734,19 @@ handle_event( NVV_EventRec* ev ) } case NVV_KEY('x'): - { - option_show_axis = !option_show_axis; - break; - } + TOGGLE_OPTION( option_show_axis, "grid axis display" ) case NVV_KEY('s'): - { - option_show_stroke = !option_show_stroke; - break; - } + TOGGLE_OPTION( option_show_stroke, "glyph stroke display" ) case NVV_KEY('g'): - { - option_show_glyph = !option_show_glyph; - break; - } + TOGGLE_OPTION( option_show_glyph, "glyph fill display" ) case NVV_KEY('d'): - { - option_show_dots = !option_show_dots; - break; - } + TOGGLE_OPTION( option_show_dots, "control points display" ) case NVV_KEY('e'): - { - option_show_em = !option_show_em; - break; - } + TOGGLE_OPTION( option_show_em, "EM square display" ) case NVV_KEY('+'): { @@ -575,51 +785,90 @@ handle_event( NVV_EventRec* ev ) } case NVV_KEY('z'): - { - ps_debug_no_vert_hints = !ps_debug_no_vert_hints; - sprintf( temp_message, "vertical hints processing is now %s", - ps_debug_no_vert_hints ? "off" : "on" ); - break; - } + TOGGLE_OPTION_NEG( ps_debug_no_vert_hints, "vertical hints processing" ) case NVV_KEY('a'): - { - ps_debug_no_horz_hints = !ps_debug_no_horz_hints; - sprintf( temp_message, "horizontal hints processing is now %s", - ps_debug_no_horz_hints ? "off" : "on" ); - break; - } + TOGGLE_OPTION_NEG( ps_debug_no_horz_hints, "horizontal hints processing" ) case NVV_KEY('Z'): - { - option_show_vert_hints = !option_show_vert_hints; - sprintf( temp_message, "vertical hints display is now %s", - option_show_vert_hints ? "off" : "on" ); - break; - } + TOGGLE_OPTION( option_show_vert_hints, "vertical hints display" ) case NVV_KEY('A'): - { - option_show_horz_hints = !option_show_horz_hints; - sprintf( temp_message, "horizontal hints display is now %s", - option_show_horz_hints ? "off" : "on" ); - break; - } + TOGGLE_OPTION( option_show_horz_hints, "horizontal hints display" ) + case NVV_KEY('S'): + TOGGLE_OPTION( option_show_smooth, "smooth points display" ); + + case NVV_KEY('b'): + TOGGLE_OPTION( option_show_blues, "blue zones display" ); case NVV_KEY('h'): - { - option_hinting = !option_hinting; - sprintf( temp_message, "hinting is now %s", - option_hinting ? "off" : "on" ); - break; - } + TOGGLE_OPTION( option_hinting, "hinting" ) + + default: + ; } } + +static void +usage() +{ + Panic( "no usage" ); +} + + +#define OPTION1(n,code) \ + case n : \ + code \ + argc--; \ + argv++; \ + break; + +#define OPTION2(n,code) \ + case n : \ + code \ + argc -= 2; \ + argv += 2; \ + break; + + +static void +parse_options( int* argc_p, char*** argv_p ) +{ + int argc = *argc_p; + char** argv = *argv_p; + + while (argc > 2 && argv[1][0] == '-') + { + switch (argv[1][1]) + { + OPTION2( 'f', first_glyph = atoi( argv[2] ); ) + + OPTION2( 's', pixel_size = atoi( argv[2] ); ) + + default: + usage(); + } + } + + *argc_p = argc; + *argv_p = argv; +} + + + int main( int argc, char** argv ) { + char* filename = "/fonts/lcdxsr.pfa"; + + parse_options( &argc, &argv ); + + if ( argc >= 2 ) + filename = argv[1]; + + /* create library */ error = nv_renderer_new( 0, &renderer ); if (error) Panic( "could not create Nirvana renderer" ); @@ -629,7 +878,7 @@ int main( int argc, char** argv ) error = nvv_display_new( renderer, &display ); if (error) Panic( "could not create display" ); - error = nvv_surface_new( display, 400, 400, nv_pixmap_type_argb, &surface ); + error = nvv_surface_new( display, 460, 460, nv_pixmap_type_argb, &surface ); if (error) Panic( "could not create surface" ); target = nvv_surface_get_pixmap( surface ); @@ -644,7 +893,7 @@ int main( int argc, char** argv ) error = FT_Init_FreeType( &freetype ); if (error) Panic( "could not initialise FreeType" ); - error = FT_New_Face( freetype, "h:/fonts/cour.pfa", 0, &face ); + error = FT_New_Face( freetype, filename, 0, &face ); if (error) Panic( "could not open font face" ); reset_size( pixel_size, grid_scale ); @@ -655,15 +904,16 @@ int main( int argc, char** argv ) { NVV_EventRec event; - glyph_index = 0; + glyph_index = first_glyph; for ( ;; ) { clear_background(); draw_grid(); - the_ps_hints = 0; + ps_debug_hints = 0; + draw_ps_blue_zones(); draw_glyph( glyph_index ); - /* draw_ps_hints(); */ + ps2_draw_control_points(); nvv_surface_refresh( surface, NULL );