diff --git a/builds/freetype.mk b/builds/freetype.mk index 1cc7e29..c73ae68 100644 --- a/builds/freetype.mk +++ b/builds/freetype.mk @@ -161,6 +161,7 @@ FT_CFLAGS = $(CPPFLAGS) \ $(CFLAGS) \ $DFT2_BUILD_LIBRARY \ $DFT_CONFIG_MODULES_H="" \ + $D_GNU_SOURCE \ $(FTOPTION_FLAG) diff --git a/configure b/configure index fd52581..bb81fb8 100755 --- a/configure +++ b/configure @@ -13,6 +13,8 @@ # Call the `configure' script located in `builds/unix'. # +export LDFLAGS="$LDFLAGS -lm" + rm -f config.mk builds/unix/unix-def.mk builds/unix/unix-cc.mk # respect GNUMAKE environment variable for backwards compatibility diff --git a/devel/ftoption.h b/devel/ftoption.h index f68b3ad..fb01836 100644 --- a/devel/ftoption.h +++ b/devel/ftoption.h @@ -607,6 +607,17 @@ FT_BEGIN_HEADER /*************************************************************************/ /* */ + /* Define FT_CONFIG_OPTION_INFINALITY_PATCHSET if you want to enable */ + /* all additional infinality patches, which are configured via env */ + /* variables. */ + /* */ + /* This option requires TT_CONFIG_OPTION_SUBPIXEL_HINTING to */ + /* defined. */ + /* */ +#define FT_CONFIG_OPTION_INFINALITY_PATCHSET + + /*************************************************************************/ + /* */ /* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version */ /* of the TrueType bytecode interpreter is used that doesn't implement */ /* any of the patented opcodes and algorithms. The patents related to */ diff --git a/include/freetype/config/ftoption.h b/include/freetype/config/ftoption.h index b481f8f..b05932a 100644 --- a/include/freetype/config/ftoption.h +++ b/include/freetype/config/ftoption.h @@ -92,7 +92,7 @@ FT_BEGIN_HEADER /* This is done to allow FreeType clients to run unmodified, forcing */ /* them to display normal gray-level anti-aliased glyphs. */ /* */ -/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ +#define FT_CONFIG_OPTION_SUBPIXEL_RENDERING /*************************************************************************/ @@ -602,11 +602,22 @@ FT_BEGIN_HEADER /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ /* defined. */ /* */ -/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ +#define TT_CONFIG_OPTION_SUBPIXEL_HINTING /*************************************************************************/ /* */ + /* Define FT_CONFIG_OPTION_INFINALITY_PATCHSET if you want to enable */ + /* all additional infinality patches, which are configured via env */ + /* variables. */ + /* */ + /* This option requires TT_CONFIG_OPTION_SUBPIXEL_HINTING to */ + /* defined. */ + /* */ +#define FT_CONFIG_OPTION_INFINALITY_PATCHSET + + /*************************************************************************/ + /* */ /* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version */ /* of the TrueType bytecode interpreter is used that doesn't implement */ /* any of the patented opcodes and algorithms. The patents related to */ diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 53851e7..c20e7d3 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -24,7 +24,10 @@ #include "afpic.h" #include "aflatin.h" #include "aferrors.h" - +#include "strings.h" +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +#include "../base/ftinf.h" +#endif #ifdef AF_CONFIG_OPTION_USE_WARPER #include "afwarp.h" @@ -40,6 +43,10 @@ #undef FT_COMPONENT #define FT_COMPONENT trace_aflatin +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +FT_Pos infinality_cur_width = 0; +#endif + /* needed for computation of round vs. flat segments */ #define FLAT_THRESHOLD( x ) ( x / 14 ) @@ -916,7 +923,10 @@ FT_Pos delta; AF_LatinAxis axis; FT_UInt nn; - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + FT_Bool adjust_heights = FALSE; + if(ftinf) adjust_heights=ftinf->autohint_increase_glyph_heights; +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ if ( dim == AF_DIMENSION_HORZ ) { @@ -944,7 +954,7 @@ { AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT]; AF_LatinBlue blue = NULL; - + int threshold = 40; for ( nn = 0; nn < Axis->blue_count; nn++ ) { @@ -954,7 +964,12 @@ break; } } - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( adjust_heights && + metrics->root.scaler.face->size->metrics.x_ppem < 15 && + metrics->root.scaler.face->size->metrics.x_ppem > 5 ) + threshold = 52; +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ if ( blue ) { FT_Pos scaled; @@ -1110,7 +1125,13 @@ /* a blue zone is only active if it is less than 3/4 pixels tall */ dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale ); + +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + /* Do at low ppems ( ~< 200 ), in order to prevent fringes */ + if ( dist <= 256 && dist >= -256 ) +#else if ( dist <= 48 && dist >= -48 ) +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ { #if 0 FT_Pos delta1; @@ -1161,7 +1182,12 @@ delta2 = -delta2; blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + /* Round to prevent fringes */ + blue->shoot.fit = FT_PIX_ROUND( blue->ref.fit - delta2 ); +#else blue->shoot.fit = blue->ref.fit - delta2; +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ #endif @@ -2015,7 +2041,10 @@ dist = edge->fpos - blue->shoot.org; if ( dist < 0 ) dist = -dist; - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + /* round down to pixels */ + /*dist = FT_MulFix( dist, scale ) & ~63;*/ +#endif dist = FT_MulFix( dist, scale ); if ( dist < best_dist ) { @@ -2190,8 +2219,17 @@ FT_Pos dist = width; FT_Int sign = 0; FT_Int vertical = ( dim == AF_DIMENSION_VERT ); - - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + FT_Int infinality_dist = 0; + FT_UInt autohint_snap_stem_height = 0; + if( ftinf ) autohint_snap_stem_height=ftinf->autohint_snap_stem_height; + if ( autohint_snap_stem_height > 100 ) + autohint_snap_stem_height = 100; + else if ( autohint_snap_stem_height < 0 ) + autohint_snap_stem_height = 0; + + if ( autohint_snap_stem_height == 0 ) +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) || axis->extra_light ) return width; @@ -2201,9 +2239,73 @@ dist = -width; sign = 1; } +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + /* Calculate snap value differently than standard freetype */ + if ( autohint_snap_stem_height > 0 && + ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || + ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) ) + { + infinality_dist = af_latin_snap_width( axis->widths, + axis->width_count, dist ); + + if ( metrics->root.scaler.face->size->metrics.x_ppem > 9 && + axis->width_count > 0 && + abs( axis->widths[0].cur - infinality_dist ) < 32 && + axis->widths[0].cur > 52 ) + { + if ( strstr( metrics->root.scaler.face->style_name, "Regular" ) || + strstr( metrics->root.scaler.face->style_name, "Book" ) || + strstr( metrics->root.scaler.face->style_name, "Medium" ) || + strcmp( metrics->root.scaler.face->style_name, "Italic" ) == 0 || + strcmp( metrics->root.scaler.face->style_name, "Oblique" ) == 0 ) + { + /* regular weight */ + if ( axis->widths[0].cur < 64 ) + infinality_dist = 64; + else if ( axis->widths[0].cur < 88 ) + infinality_dist = 64; + else if ( axis->widths[0].cur < 160 ) + infinality_dist = 128; + else if ( axis->widths[0].cur < 240 ) + infinality_dist = 190; + else infinality_dist = ( infinality_dist ) & ~63; + } + else + { + /* bold gets a different threshold */ + if ( axis->widths[0].cur < 64 ) + infinality_dist = 64 ; + else if ( axis->widths[0].cur < 108 ) + infinality_dist = 64; + else if ( axis->widths[0].cur < 160 ) + infinality_dist = 128; + else if ( axis->widths[0].cur < 222 ) + infinality_dist = 190; + else if ( axis->widths[0].cur < 288 ) + infinality_dist = 254; + else infinality_dist = ( infinality_dist + 16 ) & ~63; + } + + } + if ( infinality_dist < 52 ) + { + if ( metrics->root.scaler.face->size->metrics.x_ppem < 9 ) + { + if ( infinality_dist < 32 ) + infinality_dist = 32; + } + else + infinality_dist = 64; + } + } + else if ( autohint_snap_stem_height < 100 && + ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || + ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) ) +#else - if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || + if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ { /* smooth hinting process: very lightly quantize the stem width */ @@ -2263,6 +2365,9 @@ } } else +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( autohint_snap_stem_height < 100 ) +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ { /* strong hinting process: snap the stem width to integer pixels */ @@ -2270,7 +2375,10 @@ dist = af_latin_snap_width( axis->widths, axis->width_count, dist ); - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( autohint_snap_stem_height > 0 ) + goto Done_Width; +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ if ( vertical ) { /* in the case of vertical hinting, always round */ @@ -2333,6 +2441,32 @@ } Done_Width: +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( axis->widths[0].cur > 42 ) + /* weighted average */ + dist = (dist * ( 100 - autohint_snap_stem_height ) + + infinality_dist * autohint_snap_stem_height ) / 100; + + { + int factor = 100; + if ( axis->standard_width < 100 ) + factor = axis->standard_width; + + if ( metrics->root.scaler.face->size->metrics.x_ppem >= 9 && dist < 52 ) + dist += ( (52 - dist) * factor ) / 100; + if ( metrics->root.scaler.face->size->metrics.x_ppem < 9 && dist < 32 ) + dist += ( (32 - dist) * factor ) / 100; + + if ( axis->standard_width > 100 && + metrics->root.scaler.face->size->metrics.x_ppem >= 11 && + dist < 64 ) + dist = 64; + if ( axis->standard_width > 100 && + metrics->root.scaler.face->size->metrics.x_ppem >= 9 && + dist < 52 ) + dist = 52; + } +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ if ( sign ) dist = -dist; @@ -2354,6 +2488,8 @@ base_edge->flags, stem_edge->flags ); +/* if fitted_width causes stem_edge->pos to land basically on top of an existing + * stem_edge->pos, then add or remove 64. Need to figure out a way to do this */ stem_edge->pos = base_edge->pos + fitted_width; @@ -2915,8 +3051,11 @@ int dim; AF_LatinAxis axis; - - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + FT_Int emboldening_strength = 0; + FT_Bool use_various_tweaks = FALSE; + if( ftinf ) use_various_tweaks=ftinf->use_various_tweaks; +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ error = af_glyph_hints_reload( hints, outline ); if ( error ) goto Exit; @@ -2986,7 +3125,11 @@ } af_glyph_hints_save( hints, outline ); - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + { + infinality_cur_width = metrics->axis->widths[0].cur; + } +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ Exit: return error; } diff --git a/src/autofit/aflatin.h b/src/autofit/aflatin.h index dd75ef3..257265e 100644 --- a/src/autofit/aflatin.h +++ b/src/autofit/aflatin.h @@ -62,6 +62,9 @@ FT_BEGIN_HEADER #define AF_LATIN_MAX_WIDTHS 16 +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + extern FT_Pos infinality_cur_width; +#endif #define AF_LATIN_BLUE_ACTIVE ( 1U << 0 ) /* zone height is <= 3/4px */ #define AF_LATIN_BLUE_TOP ( 1U << 1 ) /* we have a top blue zone */ diff --git a/src/base/Jamfile b/src/base/Jamfile index e39fb09..a33e9a1 100644 --- a/src/base/Jamfile +++ b/src/base/Jamfile @@ -55,6 +55,7 @@ SubDir FT2_TOP $(FT2_SRC_DIR) base ; ftglyph ftgxval ftinit + ftinf ftlcdfil ftmm ftotval diff --git a/src/base/ftbase.c b/src/base/ftbase.c index 253dfb7..59d2d17 100644 --- a/src/base/ftbase.c +++ b/src/base/ftbase.c @@ -33,6 +33,9 @@ #include "ftstream.c" #include "fttrigon.c" #include "ftutil.c" +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +#include "ftinf.c" +#endif #ifdef FT_MACINTOSH #include "ftmac.c" diff --git a/src/base/ftinf.c b/src/base/ftinf.c new file mode 100644 index 0000000..cef285d --- /dev/null +++ b/src/base/ftinf.c @@ -0,0 +1,359 @@ +#include +#include "ftinf.h" +#define true 1 +#define false 0 + +#define on 1 +#define off 0 +#define end (-128) + +#define sw2pv 18 /* STEM_WIDTH_2_PPEM */ +#define maxp 100 /* MAX_PPEM */ + +typedef signed char pv; /* ppm and values type */ +/* the arrays start with existence flag + values */ +typedef struct sa_rules_s { + const char *name; + pv always_use_100[1+4+1]; + pv brightness[1+2+1]; + pv contrast[1+2+1]; + pv edge_detection[1+4+1]; + pv m[1+4+1]; + pv bearing_correction[1+2+1]; + pv spacing[1+5+1]; + pv start[1+5+1]; + pv stem_scaling[1+6+1]; + pv stem_translating[1+2+1]; + pv stem_translating_only[1+10+1]; + pv stem_widths[1+4]; /* these end with maxp */ + pv synthesize_stems[1+2+1]; +} sa_rules_t; + +#pragma GCC diagnostic ignored "-Wpedantic" /* C99 struct initializer tags are needed */ +#pragma GCC diagnostic ignored "-Wunused-function" + +const ftinf_t *ftinf; +/* final settings, updated from environment */ +ftinf_t _env; + +/* rules and hashing function */ +#include "ftinf_rh.c" + +/* rules selection */ +void ftinf_fill_stem_values( Stem_Data *stem_values, + const char *family, int ppem, int use_known ){ + /* set the defaults */ + stem_values->bearing_correction = TRUE; + stem_values->brightness = 0.0; + stem_values->contrast = 0.0; + stem_values->edge_detection = FALSE; + stem_values->m = -1; + stem_values->stem_scaling = -1; + stem_values->stem_spacing = -1; + stem_values->stem_start = -1; + stem_values->stem_translating = 0; + stem_values->stem_translating_only = -1024; + stem_values->stem_width = -1; + stem_values->synth_stems = FALSE; + stem_values->use_100 = FALSE; + /* pick from known rules if requested and they exist for current family */ + if( !use_known ) + return; + else { + const sa_rules_t *r=ftinf_rules( family ); + int i; + if( r==NULL ) return; + if( r->stem_widths[0]==on ) + for( i=1; r->stem_widths[i]!=maxp; ++i ) + if( ppem < r->stem_widths[i] ){ + stem_values->stem_width = i-1; + break; + } + + if( r->stem_scaling[0]==on ) + for( i=1; r->stem_scaling[i]!=end; i+=2 ) + if( ppem==r->stem_scaling[i] ){ + stem_values->stem_scaling = r->stem_scaling[i+1]; + break; + } + + if( r->m[0]==on ) + for( i=1; r->m[i]!=end; i+=2 ) + if( ppem==r->m[i] ){ + stem_values->m = r->m[i+1]; + break; + } + + if( r->stem_translating_only[0]==on ) + for( i=1; r->stem_translating_only[i]!=end; i+=2 ) + if( ppem==r->stem_translating_only[i] || r->stem_translating_only[i]==0 ){ + stem_values->stem_translating_only = r->stem_translating_only[i+1]; + break; + } + + if( r->stem_translating[0]==on ) + for( i=1; r->stem_translating[i]!=end; i+=2 ) + if( ppem==r->stem_translating[i] || r->stem_translating[i]==0 ){ + stem_values->stem_translating = r->stem_translating[i+1]; + break; + } + + if( r->always_use_100[0]==on ) + for( i=1; r->always_use_100[i]!=end; i+=2 ) + if( ppem>=r->always_use_100[i] && ppem<=r->always_use_100[i+1] ){ + stem_values->use_100 = TRUE; + break; + } + + if( r->synthesize_stems[0]==on ) + for( i=1; r->synthesize_stems[i]!=end; i+=2 ) + if( ppem>=r->synthesize_stems[i] && ppem<=r->synthesize_stems[i+1] ){ + stem_values->synth_stems = TRUE; + break; + } + + if( r->edge_detection[0]==on ) + for( i=1; r->edge_detection[i]!=end; i+=2 ) + if( ppem>=r->edge_detection[i] && ppem<=r->edge_detection[i+1] ){ + stem_values->edge_detection = TRUE; + break; + } + + if( r->bearing_correction[0]==on ) + for( i=1; r->bearing_correction[i]!=end; i+=2 ) + if( ppem>=r->bearing_correction[i] && ppem<=r->bearing_correction[i+1] ){ + stem_values->bearing_correction = FALSE; + break; + } + +#if(0) + if( r->brightness[0]==on ) + for( i=1; r->brightness[i]!=end; i+=2 ) + if( ppem==r->brightness[i]||r->brightness[i]==0 ){ + stem_values->brightness=r->brightness[i+1]*(1.0f/300.0f); + break; + } + + if( r->contrast[0]==on ) + for( i=1; r->contrast[i]!=end; i+=2 ) + if( ppem==r->contrast[i]||r->contrast[i]==0 ){ + stem_values->contrast=r->contrast[i+1]*(1.0f/300.0f); + break; + } + if( r->spacing[0]==on ){ + /* not used by original code */ + } + if( r->start[0]==on ){ + /* not used by original code */ + } +#endif + } + return; +} + +void ftinf_get_bc( const char *family, int ppem, float *brightness, float *contrast ){ + const sa_rules_t *r=ftinf_rules( family ); + *brightness=0; + *contrast=0; + if( r ){ + int i; + if( r->brightness[0]==on ) + for( i=1; r->brightness[i]!=end; i+=2 ) + if( ppem==r->brightness[i]||r->brightness[i]==0 ){ + *brightness=r->brightness[i+1]*(1.0f/300.0f); + break; + } + + if( r->contrast[0]==on ) + for( i=1; r->contrast[i]!=end; i+=2 ) + if( ppem==r->contrast[i]||r->contrast[i]==0 ){ + *contrast=r->contrast[i+1]*(1.0f/300.0f); + break; + } + } + return; +} + +static int +bool_val( const char *s ){ + if ( s != NULL ) + return strcasecmp(s, "true") == 0 + || strcasecmp(s, "1") == 0 + || strcasecmp(s, "on") == 0 + || strcasecmp(s, "yes") ==0; + else + return 0; +} + +static int +int_val( const char *s, int min, int max ){ + int val; + sscanf ( s, "%d", &val ); + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + return val; +} + +/* settings and hashing function */ +#include "ftinf_sh.c" + +/* + Get active Infinality settings + */ +void ftinf_env(){ + const char *s; + ftinf=ftinf_settings( getenv( "INFINALITY_FT" ) ); + + if( ftinf==NULL ){ + ftinf=ftinf_settings( "ultimate3" ); + /* this should always succeed */ +#if(0) + if( ftinf==NULL ){ + /* put an error here */ + exit(-1); + } +#endif + } + _env=ftinf[0]; /* copy as defaults */ + + /* check if custom environment values are set and update with them */ + s=getenv( "INFINALITY_FT_AUTOHINT_INCREASE_GLYPH_HEIGHTS" ); + if( s ) _env.autohint_increase_glyph_heights=bool_val( s ); + s=getenv( "INFINALITY_FT_AUTOHINT_SNAP_STEM_HEIGHT" ); + if( s ) _env.autohint_snap_stem_height=int_val( s, 0, 100 ); + s=getenv( "INFINALITY_FT_USE_VARIOUS_TWEAKS" ); + if( s ) _env.use_various_tweaks=bool_val( s ); + s=getenv( "INFINALITY_FT_USE_KNOWN_SETTINGS_ON_SELECTED_FONTS" ); + if( s ) _env.use_known_settings_on_selected_fonts=bool_val(s); +#if(0) /* not used (naming error also) */ + s=getenv( "INFINALITY_FT_AUTOHINT_MINIMUM_STEM_WIDTH" ); + if( s ) _env.autohint_minimum_stem_height=int_val( s, 0, 100 ); +#endif + s=getenv( "INFINALITY_FT_STEM_SNAPPING_SLIDING_SCALE" ); + if( s ) sscanf( s, "%d", &_env.stem_snapping_sliding_scale ); + s=getenv( "INFINALITY_FT_STEM_ALIGNMENT_STRENGTH" ); + if( s ) sscanf( s, "%d", &_env.stem_alignment_strength ); + s=getenv( "INFINALITY_FT_STEM_FITTING_STRENGTH" ); + if( s ) sscanf( s, "%d", &_env.stem_fitting_strength ); + s=getenv( "INFINALITY_FT_CHROMEOS_STYLE_SHARPENING_STRENGTH" ); + if( s ) _env.chromeos_style_sharpening_strength=int_val( s, 0, 100 ); + s=getenv( "INFINALITY_FT_BRIGHTNESS" ); + if( s ) sscanf( s, "%d", &_env.brightness ); + s=getenv( "INFINALITY_FT_CONTRAST" ); + if( s ) sscanf( s, "%d", &_env.contrast ); + s=getenv( "INFINALITY_FT_WINDOWS_STYLE_SHARPENING_STRENGTH" ); + if( s ) _env.windows_style_sharpening_strength=int_val( s, 0, 100 ); + s=getenv( "INFINALITY_FT_GAMMA_CORRECTION" ); + if( s ){ + float *f=_env.gamma_correction; + sscanf ( s, "%f %f", &f[0], &f[1] ); + if( f[1] < 1.0f ) f[1]=1.0f; + } + s=getenv( "INFINALITY_FT_FRINGE_FILTER_STRENGTH" ); + if( s ) sscanf( s, "%d", &_env.fringe_filter_strength ); + s=getenv( "INFINALITY_FT_GRAYSCALE_FILTER_STRENGTH" ); + if( s ) sscanf( s, "%d", &_env.grayscale_filter_strength ); + s=getenv( "INFINALITY_FT_AUTOHINT_HORIZONTAL_STEM_DARKEN_STRENGTH" ); + if( s ) sscanf( s, "%d", &_env.autohint_horizontal_stem_darken_strength ); + s=getenv( "INFINALITY_FT_AUTOHINT_VERTICAL_STEM_DARKEN_STRENGTH" ); + if( s ) sscanf( s, "%d", &_env.autohint_vertical_stem_darken_strength ); + s=getenv( "INFINALITY_FT_GLOBAL_EMBOLDEN_X_VALUE" ); + if( s ) sscanf( s, "%d", &_env.global_embolden_x_value ); + s=getenv( "INFINALITY_FT_GLOBAL_EMBOLDEN_Y_VALUE" ); + if( s ) sscanf( s, "%d", &_env.global_embolden_y_value ); + s=getenv( "INFINALITY_FT_BOLD_EMBOLDEN_X_VALUE" ); + if( s ) sscanf( s, "%d", &_env.bold_embolden_x_value ); + s=getenv( "INFINALITY_FT_BOLD_EMBOLDEN_Y_VALUE" ); + if( s ) sscanf( s, "%d", &_env.bold_embolden_y_value ); + s=getenv( "INFINALITY_FT_FILTER_PARAMS" ); + if( s ) { + int *f=_env.filter_params; + if( sscanf( s, "%d %d %d %d %d", f+1, f+2, f+3, f+4, f+5 )==5 ) + f[0]=on; + else + f[0]=off; /* FIXME: put a warning? */ + } + /* do the range verifications as in original code */ + if ( _env.stem_snapping_sliding_scale > maxp ) + _env.stem_snapping_sliding_scale = 0; + else if ( _env.stem_snapping_sliding_scale < 0 ) + _env.stem_snapping_sliding_scale = 0; + if (_env.stem_snapping_sliding_scale < 11 && + _env.stem_snapping_sliding_scale > 0 ) + _env.stem_snapping_sliding_scale = 11; + + if ( _env.stem_alignment_strength > 100 ) + _env.stem_alignment_strength = 100; + else if ( _env.stem_alignment_strength < 0 ) + _env.stem_alignment_strength = 0; + + if ( _env.stem_fitting_strength > 100 ) + _env.stem_fitting_strength = 100; + else if ( _env.stem_fitting_strength < 0 ) + _env.stem_fitting_strength = 0; + + if ( _env.chromeos_style_sharpening_strength > 100 ) + _env.chromeos_style_sharpening_strength = 100; + else if ( _env.chromeos_style_sharpening_strength < 0 ) + _env.chromeos_style_sharpening_strength = 0; + + if ( _env.brightness > 100 ) + _env.brightness = 100; + else if ( _env.brightness < -100 ) + _env.brightness = 0; + + if ( _env.contrast > 100 ) + _env.contrast = 100; + else if ( _env.contrast < -100 ) + _env.contrast = 0; + + if ( _env.windows_style_sharpening_strength > 100 ) + _env.windows_style_sharpening_strength = 100; + else if ( _env.windows_style_sharpening_strength < 0 ) + _env.windows_style_sharpening_strength = 0; + + if ( _env.fringe_filter_strength > 100 ) + _env.fringe_filter_strength = 100; + else if ( _env.fringe_filter_strength < 0 ) + _env.fringe_filter_strength = 0; + + if ( _env.grayscale_filter_strength > 100 ) + _env.grayscale_filter_strength = 100; + else if ( _env.grayscale_filter_strength < 0 ) + _env.grayscale_filter_strength = 0; + + if ( _env.autohint_horizontal_stem_darken_strength > 100 ) + _env.autohint_horizontal_stem_darken_strength = 100; + else if ( _env.autohint_horizontal_stem_darken_strength < 0 ) + _env.autohint_horizontal_stem_darken_strength = 0; + + if ( _env.autohint_vertical_stem_darken_strength > 100 ) + _env.autohint_vertical_stem_darken_strength = 100; + else if ( _env.autohint_horizontal_stem_darken_strength < 0 ) + _env.autohint_vertical_stem_darken_strength = 0; + + if ( _env.global_embolden_x_value > 128 ) + _env.global_embolden_x_value = 128; + else if ( _env.global_embolden_x_value < -128 ) + _env.global_embolden_x_value = -128; + + if ( _env.global_embolden_y_value > 128 ) + _env.global_embolden_y_value = 128; + else if ( _env.global_embolden_y_value < -128 ) + _env.global_embolden_y_value = -128; + + if ( _env.bold_embolden_x_value > 128 ) + _env.bold_embolden_x_value = 128; + else if (_env.bold_embolden_x_value < -128 ) + _env.bold_embolden_x_value = -128; + + if ( _env.bold_embolden_y_value > 128 ) + _env.bold_embolden_y_value = 128; + else if ( _env.bold_embolden_y_value < -128 ) + _env.bold_embolden_y_value = -128; + + /* point to the combined and checked settings */ + ftinf=&_env; +} diff --git a/src/base/ftinf.h b/src/base/ftinf.h new file mode 100644 index 0000000..dc35f48 --- /dev/null +++ b/src/base/ftinf.h @@ -0,0 +1,64 @@ +#ifndef _FTINF_H_ +#define _FTINF_H_ +/* + Stem snapping rules + (base freetype typedefs assumed already included) + */ +typedef struct +{ + FT_Int stem_width; + FT_Int stem_spacing; + FT_Int stem_start; + FT_Int stem_scaling; + FT_Int stem_translating_only; + FT_Int stem_translating; + float brightness; + float contrast; + FT_Bool use_100; + FT_Bool synth_stems; + FT_Bool edge_detection; + FT_Bool bearing_correction; + FT_Int m; +} Stem_Data; + +/* + Infinality settings + */ +typedef struct ftinf_s { + const char *name; + int autohint_horizontal_stem_darken_strength; + int autohint_snap_stem_height; + int autohint_increase_glyph_heights; + int autohint_vertical_stem_darken_strength; + int bold_embolden_x_value; + int bold_embolden_y_value; + int brightness; + int chromeos_style_sharpening_strength; + int contrast; + int filter_params[6]; /* 1st one used as existence flag */ + int fringe_filter_strength; + float gamma_correction[2]; + int global_embolden_x_value; + int global_embolden_y_value; + int grayscale_filter_strength; + int stem_alignment_strength; + int stem_fitting_strength; + int stem_snapping_sliding_scale; + int use_known_settings_on_selected_fonts; + int use_various_tweaks; + int windows_style_sharpening_strength; +} ftinf_t; + +extern FT_Pos infinality_cur_width; /* defined in aflatin.c */ + +extern const ftinf_t *ftinf; /* active settings */ + +extern void ftinf_fill_stem_values( Stem_Data *stem_values, + const char *family, int ppem, int use_known ); +extern void ftinf_get_bc( const char *family, int ppem, + float *brightness, float *contrast ); + +/* get values from environment (FIXME: maybe update with using user files) */ +extern void ftinf_env(); + +#endif diff --git a/src/base/ftinf_rh.c b/src/base/ftinf_rh.c new file mode 100644 index 0000000..4cd6f1a --- /dev/null +++ b/src/base/ftinf_rh.c @@ -0,0 +1,628 @@ +/* ANSI-C code produced by gperf version 3.1 */ +/* Command-line: gperf ftinf_rh.gperf */ +/* Computed positions: -k'1,$' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +#line 9 "ftinf_rh.gperf" + +#include +static const struct sa_rules_s* _rules_get( const char*str, unsigned len ); +/* maximum key range = 82, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +_rules_hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 0, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 5, 45, 5, + 35, 25, 35, 35, 50, 45, 85, 85, 0, 25, + 40, 5, 0, 85, 50, 20, 20, 0, 10, 10, + 85, 10, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85 + }; + return len + asso_values[(unsigned char)str[len - 1]] + asso_values[(unsigned char)str[0]]; +} + +#ifdef __GNUC__ +__inline +#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct sa_rules_s * +_rules_get (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 58, + MIN_WORD_LENGTH = 3, + MAX_WORD_LENGTH = 24, + MIN_HASH_VALUE = 3, + MAX_HASH_VALUE = 84 + }; + + static const struct sa_rules_s wordlist[] = + { +#line 15 "ftinf_rh.gperf" +{ .name="---", + .synthesize_stems={on, 13, 13, end} +}, +#line 253 "ftinf_rh.gperf" +{ .name="ubuntu", + .always_use_100={on, 12, 13, 15, 15, end} +}, +#line 31 "ftinf_rh.gperf" +{ .name="arial", + .always_use_100={on, 0, maxp, end}, + .edge_detection={on, 11, 11, 13, 13, end}, + .spacing={on, 10, 11, 23, 25, 30, end}, + .start={on, 11, 18, 23, 30, 30, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 16, 8, 32, 9, 32, 16, -24, end} +}, +#line 87 "ftinf_rh.gperf" +{ .name="corbel", + .stem_translating_only={on, 10, 16, end}, + .stem_widths={on, 10, 21, maxp} +}, +#line 71 "ftinf_rh.gperf" +{ .name="canwell", + .stem_scaling={on, 13, 0, end} +}, +#line 216 "ftinf_rh.gperf" +{ .name="pragmata", + .always_use_100={on, 0, maxp, end} +}, +#line 67 "ftinf_rh.gperf" +{ .name="cantarell", + .stem_translating_only={on, 11, 0, 12, 0, end}, + .stem_widths={on, 10, 22, maxp,} +}, +#line 39 "ftinf_rh.gperf" +{ .name="arimo", + .always_use_100={on, 0, maxp, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 8, 8, 32, 9, 32, end} +}, +#line 207 "ftinf_rh.gperf" +{ .name="optima", + .brightness={on, 0, -20, end}, + .contrast={on, 0, 25, end}, + .stem_scaling={on, 17, 1, end}, + .stem_translating_only={on, 10, 0, 11, 0, 12, 0, end} +}, +#line 63 "ftinf_rh.gperf" +{ .name="candara", + .stem_scaling={on, 14, 1, 17, 1, end}, + .stem_translating_only={on, 10, 16, end} +}, +#line 77 "ftinf_rh.gperf" +{ .name="comfortaa", + .stem_widths={on, 10, 19, 22, maxp}, + .stem_scaling={on, 11, 0, end} +}, +#line 161 "ftinf_rh.gperf" +{ .name="liberation mono", + .always_use_100={on, 0, maxp, end} +}, +#line 18 "ftinf_rh.gperf" +{ .name="andale mono", + .always_use_100={on, 0, maxp, end}, + .stem_scaling={on, 11, 1, end}, + .stem_widths={on, 10, 21, maxp,} +}, +#line 256 "ftinf_rh.gperf" +{ .name="verdana", + .always_use_100={on, 0, 14, 16, maxp, end}, + .stem_scaling={on, 12, 1, 15, 1, end}, + .stem_translating_only={on, 8, 16, 15, 16, 14, 32, 18, 32, 19, 24, end} +}, +#line 74 "ftinf_rh.gperf" +{ .name="century gothic", + .stem_widths={on, 10, 22, maxp,} +}, +#line 91 "ftinf_rh.gperf" +{ .name="courier new", + .always_use_100={on, 12, 12, end}, + .edge_detection={on, 10, 12, end}, + .m={on, 13, 1, 14, 1, end} +}, +#line 23 "ftinf_rh.gperf" +{ .name="arial narrow", + .stem_widths={on, 10, 21, maxp,} +}, +#line 185 "ftinf_rh.gperf" +{ .name="luxi sans", + .always_use_100={on, 13, 13, end}, + .stem_widths={on, 10, 17, sw2pv, maxp,} +}, +#line 225 "ftinf_rh.gperf" +{ .name="samba", + .stem_scaling={on, 11, 0, end} +}, +#line 233 "ftinf_rh.gperf" +{ .name="tahoma", + .always_use_100={on, 11, 11, 14, maxp, end}, + .edge_detection={on, 11, 11, end}, + .spacing={on, 10, 12, 18, 18, 30, end}, + .start={on, 14, 17, 30, 100, 100, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 7, 32, 8, 32, 9, 32, end}, +}, +#line 164 "ftinf_rh.gperf" +{ .name="liberation sans narrow", + .stem_widths={on,10, 22, maxp,} +}, +#line 81 "ftinf_rh.gperf" +{ .name="consolas", + .always_use_100={on, 0, maxp, end}, + .stem_translating_only={on, 8, 32, 9, 32, end}, + .stem_widths={on, 10, 20, maxp,}, + .stem_scaling={on, 11, 1, end} +}, +#line 203 "ftinf_rh.gperf" +{ .name="open sans", + .stem_translating_only={on, 10, 16, 9, 16, end}, + .stem_widths={on, 10, 20, maxp,} +}, +#line 167 "ftinf_rh.gperf" +{ .name="liberation sans", + .edge_detection={on, 11, 11, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 8, 8, 32, 9, 32, end}, + .stem_widths={on,10, 19, maxp,} +}, +#line 193 "ftinf_rh.gperf" +{ .name="monaco", + .always_use_100={on, 0, maxp, end} +}, +#line 101 "ftinf_rh.gperf" +{ .name="cousine", + .always_use_100={on, 0, maxp, end} +}, +#line 176 "ftinf_rh.gperf" +{ .name="lucida grande", + .stem_scaling={on, 13, 1, end}, + .stem_translating_only={on, 13, 24, 14, 24, 8, 16, 9, 16, end}, + .stem_widths={on, 10, 16, sw2pv, maxp}, +}, +#line 173 "ftinf_rh.gperf" +{ .name="lucida console", + .always_use_100={on, 0, maxp, end} +}, +#line 196 "ftinf_rh.gperf" +{ .name="myriad pro", + .stem_scaling={on, 14, 1, 17, 1, end}, + .stem_translating_only={on, 10, 16, 11, 0, 9, 16, end} +}, +#line 26 "ftinf_rh.gperf" +{ .name="arial unicode ms", + .always_use_100={on, 0, maxp, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 16, 8, 32, 9, 32, end} +}, +#line 213 "ftinf_rh.gperf" +{ .name="palatino linotype", + .edge_detection={on, 0, 100, end} +}, +#line 181 "ftinf_rh.gperf" +{ .name="lucida sans unicode", + .stem_translating_only={on, 13, 24, 14, 24, 8, 16, 9, 16, end}, + .stem_widths={on,10, 16, sw2pv, maxp,} +}, +#line 140 "ftinf_rh.gperf" +{ .name="futura", + .stem_widths={on, 10, 14, sw2pv, maxp,} +}, +#line 147 "ftinf_rh.gperf" +{ .name="georgia", + .stem_translating_only={on, 13, 16, 14, 16, 15, 0, end} +}, +#line 125 "ftinf_rh.gperf" +{ .name="freemono", + .always_use_100={on, 0, maxp, end} +}, +#line 200 "ftinf_rh.gperf" +{ .name="nina", + .stem_scaling={on, 11, 0, 12, 0, 13, 0, end} +}, +#line 121 "ftinf_rh.gperf" +{ .name="essential pragmatapro", + .always_use_100={on, 0, maxp, end}, + .m={on, 13, 0, 14, 0, end} +}, +#line 247 "ftinf_rh.gperf" +{ .name="trebuchet ms", + .always_use_100={on, 13, 13, end}, + .stem_scaling={on, 13, 0, 17, 0, 20, 1, end}, + .stem_translating_only={on, 10, 16, 11, 0, 8, 32, 9, 32, end}, + .stem_widths={on, 10, 17, sw2pv, maxp,} +}, +#line 114 "ftinf_rh.gperf" +{ .name="droid sans mono", + .m={on, 12, 0, end} +}, +#line 104 "ftinf_rh.gperf" +{ .name="dejavu sans mono", + .always_use_100={on, 0, maxp, end}, + .stem_translating_only={on, 7, 16, 8, 32, 9, 16, end} +}, +#line 57 "ftinf_rh.gperf" +{ .name="calibri", + .always_use_100={on, 23, maxp, end}, + .stem_scaling={on, 15, 1, 17, 1, 18, 1, end}, + .stem_translating_only={on, 10, 16, 15, 0, end}, + .stem_widths={on, 1, 10, 19, maxp,} +}, +#line 156 "ftinf_rh.gperf" +{ .name="inconsolata", + .stem_scaling={on, 12, 1, 15, 1, end}, + .stem_translating_only={on, 10, 24, 9, 32, end}, + .stem_widths={on, 10, 23, maxp,}, +}, +#line 96 "ftinf_rh.gperf" +{ .name="courier", + .always_use_100={on, 0, maxp, end}, + .m={on, 13, 1, 14, 1, end}, + .stem_translating_only={on, 13, 16, 15, 0, end} +}, +#line 128 "ftinf_rh.gperf" +{ .name="freesans", + .always_use_100={on, 0, maxp, end}, + .edge_detection={on, 11, 11, 13, 13, end}, + .spacing={on, 10, 12, 18, 18, 30, end}, + .start={on, 10, 18, 18, 25, 30, end}, + .stem_scaling={on, 16, 0, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 16, 9, 8, end} +}, +#line 150 "ftinf_rh.gperf" +{ .name="gill sans", + .stem_widths={on, 10, 17, sw2pv, maxp,} +}, +#line 117 "ftinf_rh.gperf" +{ .name="droid sans", + .always_use_100={on, 12, 12, 15, 15, end}, + .stem_translating_only={on, 8, 16, 9, 16, end} +}, +#line 108 "ftinf_rh.gperf" +{ .name="dejavu sans", + .always_use_100={on, 10, 14, 16, 17, end}, + .m={on, 12, 0, end}, + .stem_scaling={on, 12, 1, end}, + .stem_translating_only={on, 8, 16, 15, -20, end} +}, +#line 219 "ftinf_rh.gperf" +{ .name="raleway", + .stem_scaling={on, 15, 0, end} +}, +#line 153 "ftinf_rh.gperf" +{ .name="helvetica cy", + .stem_widths={on, 10, 23, maxp,} +}, +#line 228 "ftinf_rh.gperf" +{ .name="segoe ui", + .always_use_100={on, 11, 12, 14, 14, end}, + .stem_translating_only={on, 10, 0, 7, 32, 8, 16, 9, 24, end}, + .stem_widths={on, 10, 23, maxp,} +}, +#line 48 "ftinf_rh.gperf" +{ .name="bitstream vera sans mono", + .always_use_100={on, 0, maxp, end} +}, +#line 241 "ftinf_rh.gperf" +{ .name="times new roman", + .always_use_100={on, 14, 14, 16, 16, end}, + .bearing_correction={0, 100, end}, + .stem_scaling={on, 17, 1, end}, + .stem_translating_only={on, 17, 8, end} +}, +#line 222 "ftinf_rh.gperf" +{ .name="rokkitt", + .stem_widths={on, 10, 21, maxp,} +}, +#line 143 "ftinf_rh.gperf" +{ .name="garamond", + .brightness={on, 0, -20, end}, + .contrast={on, 0, 25, end} +}, +#line 137 "ftinf_rh.gperf" +{ .name="freeserif", + .stem_scaling={on, 13, 1, 17, 1, end} +}, +#line 189 "ftinf_rh.gperf" +{ .name="microsoft sans serif", + .always_use_100={on, 0, maxp, end}, + .stem_translating_only={on, 10, 16, 8, 32, 9, 32, end} +}, +#line 44 "ftinf_rh.gperf" +{ .name="baskerville", + .brightness={on, 0, -20, end}, + .contrast={on, 0, 25, end} +}, +#line 51 "ftinf_rh.gperf" +{ .name="bitstream vera sans", + .always_use_100={on, 10, 14, 16, 17, end}, + .m={on, 12, 0, end}, + .stem_scaling={on ,12, 1, end}, + .stem_translating_only={on, 8, 16, end} +} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = _rules_hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE) + { + register const struct sa_rules_s *resword; + + switch (key - 3) + { + case 0: + resword = &wordlist[0]; + goto compare; + case 3: + resword = &wordlist[1]; + goto compare; + case 7: + resword = &wordlist[2]; + goto compare; + case 8: + resword = &wordlist[3]; + goto compare; + case 9: + resword = &wordlist[4]; + goto compare; + case 10: + resword = &wordlist[5]; + goto compare; + case 11: + resword = &wordlist[6]; + goto compare; + case 12: + resword = &wordlist[7]; + goto compare; + case 13: + resword = &wordlist[8]; + goto compare; + case 14: + resword = &wordlist[9]; + goto compare; + case 16: + resword = &wordlist[10]; + goto compare; + case 17: + resword = &wordlist[11]; + goto compare; + case 18: + resword = &wordlist[12]; + goto compare; + case 19: + resword = &wordlist[13]; + goto compare; + case 21: + resword = &wordlist[14]; + goto compare; + case 23: + resword = &wordlist[15]; + goto compare; + case 24: + resword = &wordlist[16]; + goto compare; + case 26: + resword = &wordlist[17]; + goto compare; + case 27: + resword = &wordlist[18]; + goto compare; + case 28: + resword = &wordlist[19]; + goto compare; + case 29: + resword = &wordlist[20]; + goto compare; + case 30: + resword = &wordlist[21]; + goto compare; + case 31: + resword = &wordlist[22]; + goto compare; + case 32: + resword = &wordlist[23]; + goto compare; + case 33: + resword = &wordlist[24]; + goto compare; + case 34: + resword = &wordlist[25]; + goto compare; + case 35: + resword = &wordlist[26]; + goto compare; + case 36: + resword = &wordlist[27]; + goto compare; + case 37: + resword = &wordlist[28]; + goto compare; + case 38: + resword = &wordlist[29]; + goto compare; + case 39: + resword = &wordlist[30]; + goto compare; + case 41: + resword = &wordlist[31]; + goto compare; + case 43: + resword = &wordlist[32]; + goto compare; + case 44: + resword = &wordlist[33]; + goto compare; + case 45: + resword = &wordlist[34]; + goto compare; + case 46: + resword = &wordlist[35]; + goto compare; + case 48: + resword = &wordlist[36]; + goto compare; + case 49: + resword = &wordlist[37]; + goto compare; + case 52: + resword = &wordlist[38]; + goto compare; + case 53: + resword = &wordlist[39]; + goto compare; + case 54: + resword = &wordlist[40]; + goto compare; + case 58: + resword = &wordlist[41]; + goto compare; + case 59: + resword = &wordlist[42]; + goto compare; + case 60: + resword = &wordlist[43]; + goto compare; + case 61: + resword = &wordlist[44]; + goto compare; + case 62: + resword = &wordlist[45]; + goto compare; + case 63: + resword = &wordlist[46]; + goto compare; + case 64: + resword = &wordlist[47]; + goto compare; + case 69: + resword = &wordlist[48]; + goto compare; + case 70: + resword = &wordlist[49]; + goto compare; + case 71: + resword = &wordlist[50]; + goto compare; + case 72: + resword = &wordlist[51]; + goto compare; + case 74: + resword = &wordlist[52]; + goto compare; + case 75: + resword = &wordlist[53]; + goto compare; + case 76: + resword = &wordlist[54]; + goto compare; + case 77: + resword = &wordlist[55]; + goto compare; + case 78: + resword = &wordlist[56]; + goto compare; + case 81: + resword = &wordlist[57]; + goto compare; + } + return 0; + compare: + { + register const char *s = resword->name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return resword; + } + } + } + return 0; +} +#line 261 "ftinf_rh.gperf" + + +static const sa_rules_t* +ftinf_rules( const char *name ){ + if( name ){ + enum { + max_wlen=31 + }; + char buf[max_wlen+1]; + int len=strlen( name ); + if( len <= max_wlen ){ + const sa_rules_t *p; + int i; + for( i=0; i +static const struct sa_rules_s* _rules_get( const char*str, unsigned len ); +%} +struct sa_rules_s; +%% +{ .name="---", + .synthesize_stems={on, 13, 13, end} +}, +{ .name="andale mono", + .always_use_100={on, 0, maxp, end}, + .stem_scaling={on, 11, 1, end}, + .stem_widths={on, 10, 21, maxp,} +}, +{ .name="arial narrow", + .stem_widths={on, 10, 21, maxp,} +}, +{ .name="arial unicode ms", + .always_use_100={on, 0, maxp, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 16, 8, 32, 9, 32, end} +}, +{ .name="arial", + .always_use_100={on, 0, maxp, end}, + .edge_detection={on, 11, 11, 13, 13, end}, + .spacing={on, 10, 11, 23, 25, 30, end}, + .start={on, 11, 18, 23, 30, 30, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 16, 8, 32, 9, 32, 16, -24, end} +}, +{ .name="arimo", + .always_use_100={on, 0, maxp, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 8, 8, 32, 9, 32, end} +}, +{ .name="baskerville", + .brightness={on, 0, -20, end}, + .contrast={on, 0, 25, end} +}, +{ .name="bitstream vera sans mono", + .always_use_100={on, 0, maxp, end} +}, +{ .name="bitstream vera sans", + .always_use_100={on, 10, 14, 16, 17, end}, + .m={on, 12, 0, end}, + .stem_scaling={on ,12, 1, end}, + .stem_translating_only={on, 8, 16, end} +}, +{ .name="calibri", + .always_use_100={on, 23, maxp, end}, + .stem_scaling={on, 15, 1, 17, 1, 18, 1, end}, + .stem_translating_only={on, 10, 16, 15, 0, end}, + .stem_widths={on, 1, 10, 19, maxp,} +}, +{ .name="candara", + .stem_scaling={on, 14, 1, 17, 1, end}, + .stem_translating_only={on, 10, 16, end} +}, +{ .name="cantarell", + .stem_translating_only={on, 11, 0, 12, 0, end}, + .stem_widths={on, 10, 22, maxp,} +}, +{ .name="canwell", + .stem_scaling={on, 13, 0, end} +}, +{ .name="century gothic", + .stem_widths={on, 10, 22, maxp,} +}, +{ .name="comfortaa", + .stem_widths={on, 10, 19, 22, maxp}, + .stem_scaling={on, 11, 0, end} +}, +{ .name="consolas", + .always_use_100={on, 0, maxp, end}, + .stem_translating_only={on, 8, 32, 9, 32, end}, + .stem_widths={on, 10, 20, maxp,}, + .stem_scaling={on, 11, 1, end} +}, +{ .name="corbel", + .stem_translating_only={on, 10, 16, end}, + .stem_widths={on, 10, 21, maxp} +}, +{ .name="courier new", + .always_use_100={on, 12, 12, end}, + .edge_detection={on, 10, 12, end}, + .m={on, 13, 1, 14, 1, end} +}, +{ .name="courier", + .always_use_100={on, 0, maxp, end}, + .m={on, 13, 1, 14, 1, end}, + .stem_translating_only={on, 13, 16, 15, 0, end} +}, +{ .name="cousine", + .always_use_100={on, 0, maxp, end} +}, +{ .name="dejavu sans mono", + .always_use_100={on, 0, maxp, end}, + .stem_translating_only={on, 7, 16, 8, 32, 9, 16, end} +}, +{ .name="dejavu sans", + .always_use_100={on, 10, 14, 16, 17, end}, + .m={on, 12, 0, end}, + .stem_scaling={on, 12, 1, end}, + .stem_translating_only={on, 8, 16, 15, -20, end} +}, +{ .name="droid sans mono", + .m={on, 12, 0, end} +}, +{ .name="droid sans", + .always_use_100={on, 12, 12, 15, 15, end}, + .stem_translating_only={on, 8, 16, 9, 16, end} +}, +{ .name="essential pragmatapro", + .always_use_100={on, 0, maxp, end}, + .m={on, 13, 0, 14, 0, end} +}, +{ .name="freemono", + .always_use_100={on, 0, maxp, end} +}, +{ .name="freesans", + .always_use_100={on, 0, maxp, end}, + .edge_detection={on, 11, 11, 13, 13, end}, + .spacing={on, 10, 12, 18, 18, 30, end}, + .start={on, 10, 18, 18, 25, 30, end}, + .stem_scaling={on, 16, 0, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 16, 9, 8, end} +}, +{ .name="freeserif", + .stem_scaling={on, 13, 1, 17, 1, end} +}, +{ .name="futura", + .stem_widths={on, 10, 14, sw2pv, maxp,} +}, +{ .name="garamond", + .brightness={on, 0, -20, end}, + .contrast={on, 0, 25, end} +}, +{ .name="georgia", + .stem_translating_only={on, 13, 16, 14, 16, 15, 0, end} +}, +{ .name="gill sans", + .stem_widths={on, 10, 17, sw2pv, maxp,} +}, +{ .name="helvetica cy", + .stem_widths={on, 10, 23, maxp,} +}, +{ .name="inconsolata", + .stem_scaling={on, 12, 1, 15, 1, end}, + .stem_translating_only={on, 10, 24, 9, 32, end}, + .stem_widths={on, 10, 23, maxp,}, +}, +{ .name="liberation mono", + .always_use_100={on, 0, maxp, end} +}, +{ .name="liberation sans narrow", + .stem_widths={on,10, 22, maxp,} +}, +{ .name="liberation sans", + .edge_detection={on, 11, 11, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 10, 8, 8, 32, 9, 32, end}, + .stem_widths={on,10, 19, maxp,} +}, +{ .name="lucida console", + .always_use_100={on, 0, maxp, end} +}, +{ .name="lucida grande", + .stem_scaling={on, 13, 1, end}, + .stem_translating_only={on, 13, 24, 14, 24, 8, 16, 9, 16, end}, + .stem_widths={on, 10, 16, sw2pv, maxp}, +}, +{ .name="lucida sans unicode", + .stem_translating_only={on, 13, 24, 14, 24, 8, 16, 9, 16, end}, + .stem_widths={on,10, 16, sw2pv, maxp,} +}, +{ .name="luxi sans", + .always_use_100={on, 13, 13, end}, + .stem_widths={on, 10, 17, sw2pv, maxp,} +}, +{ .name="microsoft sans serif", + .always_use_100={on, 0, maxp, end}, + .stem_translating_only={on, 10, 16, 8, 32, 9, 32, end} +}, +{ .name="monaco", + .always_use_100={on, 0, maxp, end} +}, +{ .name="myriad pro", + .stem_scaling={on, 14, 1, 17, 1, end}, + .stem_translating_only={on, 10, 16, 11, 0, 9, 16, end} +}, +{ .name="nina", + .stem_scaling={on, 11, 0, 12, 0, 13, 0, end} +}, +{ .name="open sans", + .stem_translating_only={on, 10, 16, 9, 16, end}, + .stem_widths={on, 10, 20, maxp,} +}, +{ .name="optima", + .brightness={on, 0, -20, end}, + .contrast={on, 0, 25, end}, + .stem_scaling={on, 17, 1, end}, + .stem_translating_only={on, 10, 0, 11, 0, 12, 0, end} +}, +{ .name="palatino linotype", + .edge_detection={on, 0, 100, end} +}, +{ .name="pragmata", + .always_use_100={on, 0, maxp, end} +}, +{ .name="raleway", + .stem_scaling={on, 15, 0, end} +}, +{ .name="rokkitt", + .stem_widths={on, 10, 21, maxp,} +}, +{ .name="samba", + .stem_scaling={on, 11, 0, end} +}, +{ .name="segoe ui", + .always_use_100={on, 11, 12, 14, 14, end}, + .stem_translating_only={on, 10, 0, 7, 32, 8, 16, 9, 24, end}, + .stem_widths={on, 10, 23, maxp,} +}, +{ .name="tahoma", + .always_use_100={on, 11, 11, 14, maxp, end}, + .edge_detection={on, 11, 11, end}, + .spacing={on, 10, 12, 18, 18, 30, end}, + .start={on, 14, 17, 30, 100, 100, end}, + .stem_translating={on, 11, 32, end}, + .stem_translating_only={on, 7, 32, 8, 32, 9, 32, end}, +}, +{ .name="times new roman", + .always_use_100={on, 14, 14, 16, 16, end}, + .bearing_correction={0, 100, end}, + .stem_scaling={on, 17, 1, end}, + .stem_translating_only={on, 17, 8, end} +}, +{ .name="trebuchet ms", + .always_use_100={on, 13, 13, end}, + .stem_scaling={on, 13, 0, 17, 0, 20, 1, end}, + .stem_translating_only={on, 10, 16, 11, 0, 8, 32, 9, 32, end}, + .stem_widths={on, 10, 17, sw2pv, maxp,} +}, +{ .name="ubuntu", + .always_use_100={on, 12, 13, 15, 15, end} +}, +{ .name="verdana", + .always_use_100={on, 0, 14, 16, maxp, end}, + .stem_scaling={on, 12, 1, 15, 1, end}, + .stem_translating_only={on, 8, 16, 15, 16, 14, 32, 18, 32, 19, 24, end} +}, +%% + +static const sa_rules_t* +ftinf_rules( const char *name ){ + if( name ){ + enum { + max_wlen=31 + }; + char buf[max_wlen+1]; + int len=strlen( name ); + if( len <= max_wlen ){ + const sa_rules_t *p; + int i; + for( i=0; i' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +#line 9 "ftinf_sh.gperf" + +#include +static const struct ftinf_s* _settings_get( const char*str, unsigned len); +/* maximum key range = 37, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +_settings_hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 13, + 8, 30, 25, 20, 40, 10, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 5, 40, 0, + 0, 0, 40, 40, 10, 0, 40, 40, 15, 5, + 10, 0, 10, 40, 40, 0, 0, 0, 0, 0, + 0, 0, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40 + }; + return len + asso_values[(unsigned char)str[len - 1]] + asso_values[(unsigned char)str[0]]; +} + +#ifdef __GNUC__ +__inline +#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct ftinf_s * +_settings_get (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 22, + MIN_WORD_LENGTH = 3, + MAX_WORD_LENGTH = 14, + MIN_HASH_VALUE = 3, + MAX_HASH_VALUE = 39 + }; + + static const struct ftinf_s wordlist[] = + { +#line 76 "ftinf_sh.gperf" +{ .name="osx", + .autohint_horizontal_stem_darken_strength=10, + .autohint_vertical_stem_darken_strength=25, + .bold_embolden_x_value=16, + .brightness=10, + .contrast=20, + .filter_params={on, 3, 32, 38, 32, 3}, + .gamma_correction={1000, 80}, + .global_embolden_y_value=8, + .grayscale_filter_strength=25, +}, +#line 37 "ftinf_sh.gperf" +{ .name="ipad", + .filter_params={on, 0, 0, 100, 0, 0}, + .gamma_correction={1000, 80}, + .grayscale_filter_strength=100 +}, +#line 114 "ftinf_sh.gperf" +{ .name="shove", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=100, + .stem_fitting_strength=100, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true +}, +#line 126 "ftinf_sh.gperf" +{ .name="ubuntu", + .autohint_horizontal_stem_darken_strength=10, + .autohint_vertical_stem_darken_strength=25, + .brightness=-10, + .contrast=15, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={1000, 80}, + .use_various_tweaks=true +}, +#line 27 "ftinf_sh.gperf" +{ .name="classic", + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .filter_params={on, 6, 25, 38, 25, 6}, + .gamma_correction={0, 100}, + .use_various_tweaks=true +}, +#line 34 "ftinf_sh.gperf" +{ .name="disabled", + .gamma_correction={0, 100}, +}, +#line 100 "ftinf_sh.gperf" +{ .name="sharpened", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=25, + .stem_fitting_strength=25, + .stem_snapping_sliding_scale=40, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=65 +}, +#line 42 "ftinf_sh.gperf" +{ .name="infinality", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=25, + .stem_fitting_strength=25, + .stem_snapping_sliding_scale=40, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=5 +}, +#line 15 "ftinf_sh.gperf" +{ .name="custom", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 8, 24, 48, 24, 8}, + .gamma_correction={0, 100}, + .stem_alignment_strength=75, + .stem_fitting_strength=50, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true +}, +#line 180 "ftinf_sh.gperf" +{ .name="vanilla", + .filter_params={on, 6, 25, 38, 25, 6}, + .gamma_correction={0, 100}, +}, +#line 184 "ftinf_sh.gperf" +{ .name="windows7light", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .contrast=20, + .filter_params={on, 20, 25, 38, 25, 05}, + .fringe_filter_strength=100, + .gamma_correction={1000, 160}, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=100 +}, +#line 226 "ftinf_sh.gperf" +{ .name="windowsxplight", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .brightness=20, + .contrast=30, + .filter_params={on, 6, 25, 44, 25, 6}, + .fringe_filter_strength=100, + .gamma_correction={1000, 120}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=65 +}, +#line 64 "ftinf_sh.gperf" +{ .name="nudge", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=25, + .stem_fitting_strength=15, + .stem_snapping_sliding_scale=30, + .use_various_tweaks=true, +}, +#line 144 "ftinf_sh.gperf" +{ .name="ultimate2", + .filter_params={on, 6, 22, 36, 22, 6}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +#line 197 "ftinf_sh.gperf" +{ .name="windows7", + .filter_params={on, 20, 25, 42, 25, 06}, + .fringe_filter_strength=100, + .autohint_horizontal_stem_darken_strength=10, + .autohint_vertical_stem_darken_strength=25, + .windows_style_sharpening_strength=65, + .gamma_correction={1000, 120}, + .brightness=10, + .contrast=20, + .use_various_tweaks=true, + .autohint_snap_stem_height=100, + .use_known_settings_on_selected_fonts=true, +}, +#line 210 "ftinf_sh.gperf" +{ .name="windowsxp", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .brightness=10, + .contrast=20, + .filter_params={on, 6, 25, 44, 25, 6}, + .fringe_filter_strength=100, + .gamma_correction={1000, 120}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=65 +}, +#line 56 "ftinf_sh.gperf" +{ .name="linux", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 6, 25, 44, 25, 6}, + .gamma_correction={0, 100}, + .use_various_tweaks=true +}, +#line 135 "ftinf_sh.gperf" +{ .name="ultimate1", + .filter_params={on, 4, 22, 38, 22, 4}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +#line 87 "ftinf_sh.gperf" +{ .name="push", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=75, + .stem_fitting_strength=50, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true +}, +#line 171 "ftinf_sh.gperf" +{ .name="ultimate5", + .filter_params={on, 12, 28, 42, 28, 12}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +#line 162 "ftinf_sh.gperf" +{ .name="ultimate4", + .filter_params={on, 10, 25, 37, 25, 10}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +#line 153 "ftinf_sh.gperf" +{ .name="ultimate3", + .filter_params={on, 8, 24, 36, 24, 8}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = _settings_hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE) + { + register const struct ftinf_s *resword; + + switch (key - 3) + { + case 0: + resword = &wordlist[0]; + goto compare; + case 1: + resword = &wordlist[1]; + goto compare; + case 2: + resword = &wordlist[2]; + goto compare; + case 3: + resword = &wordlist[3]; + goto compare; + case 4: + resword = &wordlist[4]; + goto compare; + case 5: + resword = &wordlist[5]; + goto compare; + case 6: + resword = &wordlist[6]; + goto compare; + case 7: + resword = &wordlist[7]; + goto compare; + case 8: + resword = &wordlist[8]; + goto compare; + case 9: + resword = &wordlist[9]; + goto compare; + case 10: + resword = &wordlist[10]; + goto compare; + case 11: + resword = &wordlist[11]; + goto compare; + case 12: + resword = &wordlist[12]; + goto compare; + case 14: + resword = &wordlist[13]; + goto compare; + case 15: + resword = &wordlist[14]; + goto compare; + case 16: + resword = &wordlist[15]; + goto compare; + case 17: + resword = &wordlist[16]; + goto compare; + case 19: + resword = &wordlist[17]; + goto compare; + case 21: + resword = &wordlist[18]; + goto compare; + case 26: + resword = &wordlist[19]; + goto compare; + case 31: + resword = &wordlist[20]; + goto compare; + case 36: + resword = &wordlist[21]; + goto compare; + } + return 0; + compare: + { + register const char *s = resword->name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return resword; + } + } + } + return 0; +} +#line 242 "ftinf_sh.gperf" + + +static const ftinf_t* +ftinf_settings( const char *name ){ + if( name ){ + enum { + max_wlen=31 + }; + char buf[max_wlen+1]; + int len=strlen( name ); + if( len <= max_wlen ){ + const ftinf_t *p; + int i; + for( i=0; i +static const struct ftinf_s* _settings_get( const char*str, unsigned len); +%} +struct ftinf_s; +%% +{ .name="custom", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 8, 24, 48, 24, 8}, + .gamma_correction={0, 100}, + .stem_alignment_strength=75, + .stem_fitting_strength=50, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true +}, +{ .name="classic", + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .filter_params={on, 6, 25, 38, 25, 6}, + .gamma_correction={0, 100}, + .use_various_tweaks=true +}, +{ .name="disabled", + .gamma_correction={0, 100}, +}, +{ .name="ipad", + .filter_params={on, 0, 0, 100, 0, 0}, + .gamma_correction={1000, 80}, + .grayscale_filter_strength=100 +}, +{ .name="infinality", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=25, + .stem_fitting_strength=25, + .stem_snapping_sliding_scale=40, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=5 +}, +{ .name="linux", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 6, 25, 44, 25, 6}, + .gamma_correction={0, 100}, + .use_various_tweaks=true +}, +{ .name="nudge", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=25, + .stem_fitting_strength=15, + .stem_snapping_sliding_scale=30, + .use_various_tweaks=true, +}, +{ .name="osx", + .autohint_horizontal_stem_darken_strength=10, + .autohint_vertical_stem_darken_strength=25, + .bold_embolden_x_value=16, + .brightness=10, + .contrast=20, + .filter_params={on, 3, 32, 38, 32, 3}, + .gamma_correction={1000, 80}, + .global_embolden_y_value=8, + .grayscale_filter_strength=25, +}, +{ .name="push", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=75, + .stem_fitting_strength=50, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true +}, +{ .name="sharpened", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=25, + .stem_fitting_strength=25, + .stem_snapping_sliding_scale=40, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=65 +}, +{ .name="shove", + .autohint_horizontal_stem_darken_strength=10, + .autohint_increase_glyph_heights=true, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={0, 100}, + .stem_alignment_strength=100, + .stem_fitting_strength=100, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true +}, +{ .name="ubuntu", + .autohint_horizontal_stem_darken_strength=10, + .autohint_vertical_stem_darken_strength=25, + .brightness=-10, + .contrast=15, + .filter_params={on, 11, 22, 38, 22, 11}, + .gamma_correction={1000, 80}, + .use_various_tweaks=true +}, +{ .name="ultimate1", + .filter_params={on, 4, 22, 38, 22, 4}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +{ .name="ultimate2", + .filter_params={on, 6, 22, 36, 22, 6}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +{ .name="ultimate3", + .filter_params={on, 8, 24, 36, 24, 8}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +{ .name="ultimate4", + .filter_params={on, 10, 25, 37, 25, 10}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +{ .name="ultimate5", + .filter_params={on, 12, 28, 42, 28, 12}, + .fringe_filter_strength=25, + .gamma_correction={0, 100}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .use_various_tweaks=true, + .windows_style_sharpening_strength=25 +}, +{ .name="vanilla", + .filter_params={on, 6, 25, 38, 25, 6}, + .gamma_correction={0, 100}, +}, +{ .name="windows7light", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .contrast=20, + .filter_params={on, 20, 25, 38, 25, 05}, + .fringe_filter_strength=100, + .gamma_correction={1000, 160}, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=100 +}, +{ .name="windows7", + .filter_params={on, 20, 25, 42, 25, 06}, + .fringe_filter_strength=100, + .autohint_horizontal_stem_darken_strength=10, + .autohint_vertical_stem_darken_strength=25, + .windows_style_sharpening_strength=65, + .gamma_correction={1000, 120}, + .brightness=10, + .contrast=20, + .use_various_tweaks=true, + .autohint_snap_stem_height=100, + .use_known_settings_on_selected_fonts=true, +}, +{ .name="windowsxp", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .brightness=10, + .contrast=20, + .filter_params={on, 6, 25, 44, 25, 6}, + .fringe_filter_strength=100, + .gamma_correction={1000, 120}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=65 +}, +{ .name="windowsxplight", + .autohint_horizontal_stem_darken_strength=10, + .autohint_snap_stem_height=100, + .autohint_vertical_stem_darken_strength=25, + .brightness=20, + .contrast=30, + .filter_params={on, 6, 25, 44, 25, 6}, + .fringe_filter_strength=100, + .gamma_correction={1000, 120}, + .stem_alignment_strength=15, + .stem_fitting_strength=15, + .stem_snapping_sliding_scale=30, + .use_known_settings_on_selected_fonts=true, + .use_various_tweaks=true, + .windows_style_sharpening_strength=65 +}, +%% + +static const ftinf_t* +ftinf_settings( const char *name ){ + if( name ){ + enum { + max_wlen=31 + }; + char buf[max_wlen+1]; + int len=strlen( name ); + if( len <= max_wlen ){ + const ftinf_t *p; + int i; + for( i=0; i +#include +#include +#include "ftinf.h" #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING @@ -305,11 +308,36 @@ FT_Library_SetLcdFilter( FT_Library library, FT_LcdFilter filter ) { - static const FT_Byte default_filter[5] = - { 0x08, 0x4d, 0x56, 0x4d, 0x08 }; static const FT_Byte light_filter[5] = { 0x00, 0x55, 0x56, 0x55, 0x00 }; - +#ifndef FT_CONFIG_OPTION_INFINALITY_PATCHSET + static const FT_Byte default_filter[5] = + { 0x08, 0x4d, 0x56, 0x4d, 0x08 }; +#else + FT_Byte default_filter[5]; + if( ftinf && ftinf->filter_params[0] ) + { + const int *f=ftinf->filter_params; + /* Assume we were given integers [0-100] get them to [0-255] */ + int val; /* 2611=2.55*1024 */ + val=(f[1]*2611+512)>>10; if( val > 255 ) val=255; + default_filter[0] = (FT_Byte) val; + val=(f[2]*2611+512)>>10; if( val > 255 ) val=255; + default_filter[1] = (FT_Byte) val; + val=(f[3]*2611+512)>>10; if( val > 255 ) val=255; + default_filter[2] = (FT_Byte) val; + val=(f[4]*2611+512)>>10; if( val > 255 ) val=255; + default_filter[3] = (FT_Byte) val; + val=(f[5]*2611+512)>>10; if( val > 255 ) val=255; + default_filter[4] = (FT_Byte) val; + } else { + default_filter[0]=0x08; + default_filter[1]=0x4d; + default_filter[2]=0x56; + default_filter[3]=0x4d; + default_filter[4]=0x08; + } +#endif if ( !library ) return FT_THROW( Invalid_Library_Handle ); diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index ea40396..7156a0b 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -40,7 +40,9 @@ #ifdef FT_CONFIG_OPTION_MAC_FONTS #include "ftbase.h" #endif - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +#include "ftinf.h" +#endif #ifdef FT_DEBUG_LEVEL_TRACE @@ -78,6 +80,11 @@ #define GRID_FIT_METRICS +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +#include +#include +#include "../autofit/aflatin.h" +#endif FT_BASE_DEF( FT_Pointer ) ft_service_list_lookup( FT_ServiceDesc service_descriptors, @@ -554,6 +561,25 @@ ft_lookup_glyph_renderer( FT_GlyphSlot slot ); +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + static void + ft_glyphslot_enlarge_metrics( FT_GlyphSlot slot, + FT_Render_Mode mode ) + { + FT_Glyph_Metrics* metrics = &slot->metrics; + FT_Pos enlarge_cbox = 0; + + + /* enlarge for grayscale rendering */ + if ( mode == FT_RENDER_MODE_NORMAL ) + enlarge_cbox = 64; + + metrics->horiBearingX -= enlarge_cbox; + metrics->width += 2 * enlarge_cbox; + } +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ + + #ifdef GRID_FIT_METRICS static void ft_glyphslot_grid_fit_metrics( FT_GlyphSlot slot, @@ -612,8 +638,18 @@ FT_Bool autohint = FALSE; FT_Module hinter; TT_Face ttface = (TT_Face)face; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + FT_Bool use_various_tweaks = FALSE; + if( ftinf ) use_various_tweaks=ftinf->use_various_tweaks; + /* Force autohint if no tt instructions */ + /* NOTE: NEEDS TO BE RUN LATER IN CODE???? */ + /*if ( use_various_tweaks && + ttface->num_locations && + ttface->max_profile.maxSizeOfInstructions == 0 ) + load_flags |= FT_LOAD_FORCE_AUTOHINT;*/ +#endif if ( !face || !face->size || !face->glyph ) return FT_THROW( Invalid_Face_Handle ); @@ -702,6 +738,18 @@ { FT_AutoHinter_Interface hinting; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( use_various_tweaks ) + { + /* Force slight hinting over full hinting always */ + load_flags &= ~FT_LOAD_TARGET_LCD; + load_flags &= ~FT_LOAD_TARGET_LCD_V; + load_flags &= ~FT_LOAD_TARGET_MONO; + load_flags &= ~FT_LOAD_TARGET_NORMAL; + load_flags |= FT_LOAD_TARGET_LIGHT; + /*printf("%d ", load_flags);*/ + } +#endif /* try to load embedded bitmaps first if available */ /* */ @@ -747,6 +795,18 @@ if ( error ) goto Exit; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + infinality_cur_width = 0; + + { + /* fix for sdl_ttf */ + FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags ); + + if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) + ft_glyphslot_enlarge_metrics( slot, mode ); + } +#endif + if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) { /* check that the loaded outline is correct */ diff --git a/src/base/ftoutln.c b/src/base/ftoutln.c index 201ceab..4c8746d 100644 --- a/src/base/ftoutln.c +++ b/src/base/ftoutln.c @@ -29,7 +29,9 @@ #include FT_INTERNAL_CALC_H #include FT_INTERNAL_DEBUG_H #include FT_TRIGONOMETRY_H - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +#include "ftinf.h" +#endif /*************************************************************************/ /* */ @@ -910,7 +912,13 @@ FT_Vector* points; FT_Int c, first, last; FT_Int orientation; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + FT_Bool use_various_tweaks = FALSE; + if( ftinf ) use_various_tweaks=ftinf->use_various_tweaks; + if ( use_various_tweaks ) + ystrength = FT_PIX_FLOOR ( ystrength ); +#endif if ( !outline ) return FT_THROW( Invalid_Outline ); diff --git a/src/base/ftsynth.c b/src/base/ftsynth.c index cd68533..aa68c88 100644 --- a/src/base/ftsynth.c +++ b/src/base/ftsynth.c @@ -22,7 +22,9 @@ #include FT_INTERNAL_OBJECTS_H #include FT_OUTLINE_H #include FT_BITMAP_H - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +#include "ftinf.h" +#endif /*************************************************************************/ /* */ @@ -93,7 +95,10 @@ FT_Face face; FT_Error error; FT_Pos xstr, ystr; - +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + FT_Bool use_various_tweaks = FALSE; + if( ftinf ) use_various_tweaks=ftinf->use_various_tweaks; +#endif if ( !slot ) return; @@ -111,8 +116,16 @@ ystr = xstr; if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) + { +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( use_various_tweaks ) + (void)FT_Outline_EmboldenXY( &slot->outline, + xstr, + FT_PIX_FLOOR( ystr ) ); + else +#endif FT_Outline_EmboldenXY( &slot->outline, xstr, ystr ); - + } else /* slot->format == FT_GLYPH_FORMAT_BITMAP */ { /* round to full pixels */ @@ -150,6 +163,9 @@ slot->metrics.width += xstr; slot->metrics.height += ystr; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( !use_various_tweaks ) +#endif slot->metrics.horiAdvance += xstr; slot->metrics.vertAdvance += ystr; slot->metrics.horiBearingY += ystr; diff --git a/src/base/rules.mk b/src/base/rules.mk index 1852e08..f7ae0e6 100644 --- a/src/base/rules.mk +++ b/src/base/rules.mk @@ -41,6 +41,7 @@ BASE_SRC := $(BASE_DIR)/basepic.c \ $(BASE_DIR)/ftcalc.c \ $(BASE_DIR)/ftdbgmem.c \ $(BASE_DIR)/ftgloadr.c \ + $(BASE_DIR)/ftinf.c \ $(BASE_DIR)/ftobjs.c \ $(BASE_DIR)/ftoutln.c \ $(BASE_DIR)/ftpic.c \ diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c index 3620550..b5b3056 100644 --- a/src/smooth/ftsmooth.c +++ b/src/smooth/ftsmooth.c @@ -26,6 +26,18 @@ #include "ftsmerrs.h" +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +#include +#include FT_BITMAP_H +#include +#include +#include FT_OUTLINE_H +#include "../base/ftinf.h" + +#define verbose FALSE +#define STVALUES if (verbose) \ + printf ( "scale:%f translate:%ld ", *scale_value, *translate_value ); +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ /* initialize renderer -- init its raster */ static FT_Error @@ -34,65 +46,2278 @@ FT_Library library = FT_MODULE_LIBRARY( render ); - render->clazz->raster_class->raster_reset( render->raster, - library->raster_pool, - library->raster_pool_size ); + render->clazz->raster_class->raster_reset( render->raster, + library->raster_pool, + library->raster_pool_size ); + + return 0; + } + + + /* sets render-specific mode */ + static FT_Error + ft_smooth_set_mode( FT_Renderer render, + FT_ULong mode_tag, + FT_Pointer data ) + { + /* we simply pass it to the raster */ + return render->clazz->raster_class->raster_set_mode( render->raster, + mode_tag, + data ); + } + + /* transform a given glyph image */ + static FT_Error + ft_smooth_transform( FT_Renderer render, + FT_GlyphSlot slot, + const FT_Matrix* matrix, + const FT_Vector* delta ) + { + FT_Error error = FT_Err_Ok; + + + if ( slot->format != render->glyph_format ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + if ( matrix ) + FT_Outline_Transform( &slot->outline, matrix ); + + if ( delta ) + FT_Outline_Translate( &slot->outline, delta->x, delta->y ); + + Exit: + return error; + } + + + /* return the glyph's control box */ + static void + ft_smooth_get_cbox( FT_Renderer render, + FT_GlyphSlot slot, + FT_BBox* cbox ) + { + FT_MEM_ZERO( cbox, sizeof ( *cbox ) ); + + if ( slot->format == render->glyph_format ) + FT_Outline_Get_CBox( &slot->outline, cbox ); + } + +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + static FT_Fixed FT_FixedFromFloat(float f) + { + short value = f; + unsigned short fract = (f - value) * 0xFFFF; + + + return (FT_Fixed)((long)value << 16 | (unsigned long)fract ); + } + + + /* ChromeOS sharpening algorithm */ + /* soften the sub-pixel anti-aliasing and sharpen */ + static void + _ft_lcd_chromeos_sharpen( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_Byte cutoff, + double gamma_value ) + { + static FT_Bool initialized_gamma = FALSE; + static unsigned short gamma_ramp[256]; + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + int ii; + + if ( !initialized_gamma ) + { + initialized_gamma = TRUE; + /* linear to voltage */ + for ( ii = 0; ii < 256; ii++ ) + { + gamma_ramp[ii] = (unsigned char) + ( pow( (double)ii / 255.0, gamma_value ) * 255.0f ); + if ( gamma_ramp[ii] < cutoff ) + gamma_ramp[ii] = 0; + } + } + + /* horizontal in-place sub-pixel sharpening filter */ + if ( mode == FT_RENDER_MODE_LCD ) + { + FT_Byte* line = bitmap->buffer; + + + for ( ; height > 0; height--, line += bitmap->pitch ) + { + FT_UInt xx; + + + for ( xx = 0; xx < width; xx++ ) + line[xx] = gamma_ramp[line[xx]]; + } + } + } + + /* simple linear scale to handle various sliding values */ + float + sliding_scale ( int min_value, + int max_value, + float min_amount, + float max_amount, + int cur_value ) + { + + float m = ( min_amount - max_amount ) / (float)( min_value - max_value ); + float result = ( ( (float)cur_value * m) + ( max_amount - max_value * m ) ) ; + + if ( min_amount < max_amount ) + { + if ( result < min_amount ) + return min_amount; + if ( result > max_amount ) + return max_amount; + } + else + { + if ( result < max_amount ) + return max_amount; + if ( result > min_amount ) + return min_amount; + } + + return result; + } + + + /* brightness and contrast adjustment on the bitmap */ + static FT_Bool + _ft_bitmap_bc ( FT_Bitmap* bitmap, + float brightness, + float contrast ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* line = bitmap->buffer; + FT_UInt xx; + + + if ( brightness == 0 && contrast == 0 ) + return FALSE; + + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch ) + { + for ( xx = 0; xx < width - 1; xx += 1 ) + { + if ( line[xx] > 0) + { + float value = (float)( 255 - line[xx] ) / 256.0; + FT_Int result = 0; + + if ( brightness < 0.0 ) + value = value * ( 1.0 + brightness ); + else + value = value + ( ( 1.0 - value ) * brightness ); + + value = ( value - 0.5 ) * + ( tan ( ( contrast + 1.0 ) * 3.141592/4.0 ) ) + 0.5; + + result = (FT_Int)( 255.0 - value * 256.0 ); + + if ( result < 0 ) + result = 0; + if ( result > 255 ) + result = 255; + + line[xx] = result; + } + } + } + return TRUE; + } + + + /* Filter to mimic Windows-style sharpening */ + /* Determined via 100% experimentation. */ + static void + _ft_lcd_windows_sharpen( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_UInt strength, + FT_Library library ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* new_line; + FT_Byte* line = bitmap->buffer; + FT_Bitmap new_bitmap; + + + FT_Bitmap_New( &new_bitmap ); + FT_Bitmap_Copy( library, bitmap, &new_bitmap ); + new_line = (&new_bitmap)->buffer; + + if (strength > 0) + for (height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch, new_line += bitmap->pitch ) + { + FT_UInt xx, threshold = 128; + FT_Byte* prevline = line - bitmap->pitch; + FT_Byte* nextline = line + bitmap->pitch; + FT_Byte* new_prevline = new_line - bitmap->pitch; + FT_Byte* new_nextline = new_line + bitmap->pitch; + + for ( xx = 1; xx < width - 1; xx += 1 ) + { + /* subpixel grid sp11 sp21 sp31 */ + /* where sp22 is sp12 sp22 sp32 */ + /* current subpixel. sp13 sp23 sp33 */ + + FT_Int prevtotal, nexttotal, lefttotal, righttotal, sidesdiff, + prevdiff, nextdiff, sp11, sp21, sp31, sp12, sp22, sp32, + sp13, sp23, sp33; + + sp12 = line [xx-1]; + sp22 = line [xx]; + sp32 = line [xx+1]; + + if ( height == bitmap->rows ) + { + prevtotal = sp11 = sp21 = sp31 = 0; + prevdiff = sp22; + lefttotal = sp12 + sp13; + righttotal = sp32 + sp33; + } + else + { + prevtotal = prevline[xx-1] + prevline[xx] + prevline[xx+1]; + sp11 = prevline [xx-1]; + sp21 = prevline [xx]; + sp31 = prevline [xx+1]; + prevdiff = sp22 - sp21; + lefttotal = sp11 + sp12 + sp13; + righttotal = sp31 + sp32 + sp33; + } + + if ( height == 1 ) + { + nexttotal = sp13 = sp23 = sp33 = 0; + nextdiff = sp22; + lefttotal = sp11 + sp12; + righttotal = sp31 + sp32; + } + else + { + nexttotal = nextline[xx-1] + nextline[xx] + nextline[xx+1]; + sp13 = nextline [xx-1]; + sp23 = nextline [xx]; + sp33 = nextline [xx+1]; + nextdiff = sp23 - sp22; + lefttotal = sp11 + sp12 + sp13; + righttotal = sp31 + sp32 + sp33; + } + + sidesdiff = lefttotal - righttotal; + + if ( sidesdiff < 0 ) + sidesdiff *= -1; + + if ( prevdiff < 0 ) + prevdiff *= -1; + + if ( nextdiff < 0 ) + nextdiff *= -1; + + /* if the current pixel is less than threshold, and greater than 0 */ + if ( sp22 <= threshold && sp22 > 0 ) + { + /* A pixel is horizontally isolated if: */ + /* 1: All upper adjecent pixels are >= threshold */ + if ( prevtotal >= nexttotal && + abs( sp11 - sp12 ) > 5 && + abs( sp21 - sp22 ) > 5 && + abs( sp31 - sp32 ) > 5 && /* not a vert stem end */ + sp11 >= threshold && + sp21 >= threshold && + sp31 >= threshold && + abs( sp23 - sp22 ) > 15 ) /* not on a vert stem */ + { + /* darken upper adjacent subpixel; lighten current */ + if ( height != (FT_UInt)bitmap->rows ) + new_prevline[xx] += ( ( 255 - new_prevline[xx] ) + * strength ) / 100 ; + + new_line[xx] -= ( new_line[xx] * strength ) / 100; + + if ( height != 1 && height != (FT_UInt)bitmap->rows ) + if ( new_nextline[xx] > 155 + ( 100 - strength ) ) + new_prevline[xx] = 255; + + } + else if ( nexttotal > prevtotal && + abs( sp13 - sp12 ) > 5 && + abs( sp23 - sp22 ) > 5 && + abs( sp33 - sp32 ) > 5 && + /* 2: All lower adjecent pixels are >= threshold */ + sp13 >= threshold && + sp23 >= threshold && + sp33 >= threshold && + abs( sp22 - sp21 ) > 15 ) + { + /* darken lower adjacent subpixel; lighten current */ + if ( height != 1 ) + new_nextline[xx] += ( 255 - new_nextline[xx] ) * strength / 100; + + new_line[xx] -= ( new_line[xx] * strength ) / 100; + + if ( height != 1 ) + if ( new_nextline[xx] > 155 + ( 100 - strength ) ) + new_nextline[xx] = 255; + + } + } + else if ( sp22 > threshold && sp22 < 255 ) + { + if ( sp11 <= threshold && + abs( sp13 - sp12 ) > 5 && + abs( sp23 - sp22 ) > 5 && + abs( sp33 - sp32 ) > 5 && + sp21 <= threshold && + sp31 <= threshold && + prevtotal <= nexttotal && + abs( sp22 - sp21 ) > 15 ) + { + /* bring this subpixel 1/3 of the way to 255 at 100% strength */ + new_line[xx] += ( strength * ( 255 - new_line[xx] ) ) / 100; + + if ( height != (FT_UInt)bitmap->rows ) + new_prevline[xx] -= ( new_prevline[xx] * strength ) / 300; + } + else if ( sp13 <= threshold && + abs( sp11 - sp12 ) > 5 && + abs( sp21 - sp22 ) > 5 && + abs( sp31 - sp32 ) > 5 && + sp23 <= threshold && + sp33 <= threshold && + nexttotal < prevtotal && + abs( sp23 - sp22 ) > 15 ) + { + new_line[xx] += ( strength * ( 255 - new_line[xx] ) ) / 100; + + if ( height != 1 ) + new_nextline[xx] -= ( new_nextline[xx] * strength ) / 300; + } + } + } + } + FT_Bitmap_Copy( library, &new_bitmap, bitmap); + FT_Bitmap_Done( library, &new_bitmap ); + } + + + static void + _ft_lcd_darken_x ( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_UInt strength, + FT_Library library ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* new_line; + FT_Byte* line = bitmap->buffer; + FT_Bitmap new_bitmap; + int factor1, factor2; + int bias = 0; + + FT_Bitmap_New( &new_bitmap ); + + FT_Bitmap_Copy( library, bitmap, &new_bitmap ); + new_line = (&new_bitmap)->buffer; + + if ( strength > 0 ) + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch, new_line += bitmap->pitch ) + { + FT_UInt xx; + FT_Byte* prevline = line - bitmap->pitch; + FT_Byte* nextline = line + bitmap->pitch; + + for ( xx = 1; xx < width - 1; xx += 1 ) + { + /* subpixel grid sp11 sp21 sp31 */ + /* where sp22 is sp12 sp22 sp32 */ + /* current subpixel. sp13 sp23 sp33 */ + + FT_Int sp21, sp12, sp22, sp32, sp23; + + sp12 = line [xx-1]; + sp22 = line [xx]; + sp32 = line [xx+1]; + + if ( height == bitmap->rows ) + sp21 = 0; + else + sp21 = prevline [xx]; + + if ( height == 1 ) + sp23 = 0; + else + sp23 = nextline [xx]; + + /* darken subpixel if neighbor above and below are much less than */ + /* safer but less effective */ + factor1 = 5; + factor2 = 5; + + /* make matches in the middle of glyph slightly darker */ + /*if (height > 1 && height < (FT_UInt)bitmap->rows) bias = 1;*/ + + if ( sp22 > factor1 * sp21 && + sp22 > factor1 * sp23 && + sp22 > factor2 && + sp12 > 16 && + sp32 > 16 ) + if ( new_line[xx] < ( strength * 255 ) / 100 ) + new_line[xx] = (strength * 255 ) / 100 + + bias * ( 255 - ( strength * 255 ) / 100 ) / 3; + + } + } + FT_Bitmap_Copy( library, &new_bitmap, bitmap ); + FT_Bitmap_Done( library, &new_bitmap ); + } + + + static void + _ft_lcd_darken_y ( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_UInt strength, + FT_Library library ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* new_line; + FT_Byte* line = bitmap->buffer; + FT_Bitmap new_bitmap; + + + FT_Bitmap_New( &new_bitmap ); + FT_Bitmap_Copy( library, bitmap, &new_bitmap ); + new_line = (&new_bitmap)->buffer; + + if ( strength > 0 ) + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch, new_line += bitmap->pitch ) + { + FT_UInt xx; + + + for ( xx = 1; xx < width - 1; xx += 1 ) + { + if ( line[xx] > line[xx-1] && line[xx] > line[xx+1] ) + { + if (new_line[xx] > 0) + new_line[xx] += ( strength * ( 255 - new_line[xx] ) ) / 100; + new_line[xx-1] += ( strength * ( 255 - line[xx-1] ) ) / 100; + new_line[xx+1] += ( strength * ( 255 - line[xx+1] ) ) / 100; + } + } + } + FT_Bitmap_Copy( library, &new_bitmap, bitmap ); + FT_Bitmap_Done( library, &new_bitmap ); + } + + + static void + _ft_bitmap_cap ( FT_Bitmap* bitmap, + FT_UInt strength, + FT_Library library ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* new_line; + FT_Byte* line = bitmap->buffer; + FT_UInt cur_value = 0; + FT_Bitmap new_bitmap; + + + FT_Bitmap_New( &new_bitmap ); + FT_Bitmap_Copy( library, bitmap, &new_bitmap ); + new_line = (&new_bitmap)->buffer; + + if ( strength > 0 ) + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch, new_line += bitmap->pitch ) + { + FT_UInt xx; + + + for ( xx = 1; xx < width - 1; xx += 1 ) + { + cur_value = ( new_line[xx-1] + new_line[xx] + new_line[xx+1] ) / 3; + if ( cur_value > ( strength * 255 ) / 100 ) + { + FT_UInt new_factor = ( strength * 255 ) / 100; + new_line[xx] = ( new_line[xx] * new_factor ) / cur_value; + new_line[xx+1] = ( new_line[xx+1] * new_factor ) / cur_value; + new_line[xx-1] = ( new_line[xx-1] * new_factor ) / cur_value; + } + } + } + FT_Bitmap_Copy( library, &new_bitmap, bitmap ); + FT_Bitmap_Done( library, &new_bitmap ); + } + + static int + pseudo_gamma ( int val, float value ) + { + return 256 * ( 1.0f - powf( ( 1.0f - val * (1.0f/256.0f) ), 1.0f / value ) ); + } + +#if(0) + static void + _ft_bitmap_embolden ( FT_Bitmap* bitmap, + FT_UInt strength, + FT_Library library ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* new_line; + FT_Byte* line = bitmap->buffer; + FT_Bitmap new_bitmap; + FT_UInt xx; + + + FT_Bitmap_New(&new_bitmap); + FT_Bitmap_Copy(library, bitmap, &new_bitmap); + new_line = (&new_bitmap)->buffer; + + if ( strength > 0 ) + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch, new_line += bitmap->pitch ) + { + for ( xx = 1; xx < width - 1; xx += 1 ) + { + FT_Int new_value = 0; + + + new_value = ( strength * line [xx-1] ) / 100 + + pseudo_gamma( line [xx], .75 ) + + (strength * line [xx+1] ) / 100; + if ( new_value > 255 ) + new_value = 255; + + new_line[xx] = new_value; + } + } + FT_Bitmap_Copy( library, &new_bitmap, bitmap ); + FT_Bitmap_Done( library, &new_bitmap ); + } + + + + static void + _ft_bitmap_gamma ( FT_Bitmap* bitmap, + float strength ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* line = bitmap->buffer; + FT_UInt xx; + + + if ( strength > 0 ) + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch ) + { + + for ( xx = 1; xx < width - 1; xx += 1 ) + { + if ( abs( line[xx-1] - line[xx] ) < 20 || + abs( line[xx+1] - line[xx] ) < 20 ) + line [xx] = pseudo_gamma( line [xx], strength ) ; + } + } + } +#endif + + /* Fringe filter */ + static void + _ft_lcd_fringe_filter ( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_UInt strength, + FT_Library library ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* new_line; + FT_Byte* line = bitmap->buffer; + FT_Bitmap new_bitmap; + + + FT_Bitmap_New(&new_bitmap); + + line = bitmap->buffer; + FT_Bitmap_Copy( library, bitmap, &new_bitmap ); + new_line = (&new_bitmap)->buffer; + + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch, new_line += bitmap->pitch ) + { + /* Threshold set to 1/2 pixel intensity */ + FT_UInt xx, threshold = 128; + + /* Hack to make this work when bitmap is at first or last line */ + FT_Int fudge = bitmap->pitch * (height == (FT_UInt)bitmap->rows); + + FT_Byte* prevline = line - bitmap->pitch + fudge; + FT_Byte* nextline = line + bitmap->pitch; + + + for ( xx = 1; xx < width - 1; xx += 1 ) + { + /* subpixel grid sp11 sp21 sp31 */ + /* where sp22 is sp12 sp22 sp32 */ + /* current subpixel. sp13 sp23 sp33 */ + + FT_Int prevtotal, nexttotal, lefttotal, righttotal, sidesdiff, + leftdiff, rightdiff, prevdiff, nextdiff, sp11, sp21, sp31, + sp12, sp22, sp32, sp13, sp23, sp33; + + sp12 = line [xx-1]; + sp22 = line [xx]; + sp32 = line [xx+1]; + + /* if at max height fake out some values */ + if ( height == (FT_UInt)bitmap->rows ) + { + prevtotal = sp11 = sp21 = sp31 = 0; + prevdiff = sp22; + lefttotal = sp12 + sp13; + righttotal = sp32 + sp33; + } + else + { + prevtotal = prevline[xx-1] + prevline[xx] + prevline[xx+1]; + sp11 = prevline [xx-1]; + sp21 = prevline [xx]; + sp31 = prevline [xx+1]; + prevdiff = sp22 - sp21; + lefttotal = sp11 + sp12 + sp13; + righttotal = sp31 + sp32 + sp33; + } + + /* if at min height fake out some values */ + if ( height == 1 ) + { + nexttotal = sp13 = sp23 = sp33 = 0; + nextdiff = sp22; + lefttotal = sp11 + sp12; + righttotal = sp31 + sp32; + } + else + { + nexttotal = nextline[xx-1] + nextline[xx] + nextline[xx+1]; + sp13 = nextline [xx-1]; + sp23 = nextline [xx]; + sp33 = nextline [xx+1]; + nextdiff = sp23 - sp22; + lefttotal = sp11 + sp12 + sp13; + righttotal = sp31 + sp32 + sp33; + } + + sidesdiff = lefttotal - righttotal; + leftdiff = sp22 - sp12; + rightdiff = sp32 - sp22; + + if ( sidesdiff < 0 ) + sidesdiff *= -1; + + if ( prevdiff < 0 ) + prevdiff *= -1; + + if ( nextdiff < 0 ) + nextdiff *= -1; + + if ( leftdiff < 0 ) + leftdiff *= -1; + + if ( rightdiff < 0 ) + rightdiff *= -1; + + /* if the current subpixel is less than threshold, and varies only + slightly to left or right, lighten it */ + if ( sp22 <= threshold && sp22 > 0 && + ( leftdiff < 10 || rightdiff < 10 ) ) + { + /* A pixel is horizontally isolated if: */ + /* 1: All upper adjecent subpixels are >= threshold and all lower + adjacent ones are essentially white */ + if ( prevtotal >= nexttotal && + sp11 >= threshold && + sp21 >= threshold && + sp31 >= threshold && + sp13 < 2 && + sp23 < 2 && + sp33 < 2 ) + + { + new_line[xx] -= ( new_line[xx] * strength ) / 100; + + if ( leftdiff < 10 ) + /* OPPORTUNITY FOR IMPROVEMENT - keep going left until 255? */ + new_line[xx-1] -= ( new_line[xx-1] * strength ) / 200; + + if ( rightdiff < 10 ) + /* OPPORTUNITY FOR IMPROVEMENT */ + new_line[xx+1] -= ( new_line[xx+1] * strength ) / 200; + } + else if ( nexttotal > prevtotal && + /* 2: the inverse of above */ + sp13 >= threshold && + sp23 >= threshold && + sp33 >= threshold && + sp11 < 2 && + sp21 < 2 && + sp31 < 2 ) + { + new_line[xx] -= ( new_line[xx] * strength ) / 100; + + if ( leftdiff < 10 ) + /* OPPORTUNITY FOR IMPROVEMENT - keep going left until 255? */ + new_line[xx-1] -= ( new_line[xx-1] * strength ) / 200; + + if ( rightdiff < 10 ) + /* OPPORTUNITY FOR IMPROVEMENT */ + new_line[xx+1] -= ( new_line[xx+1] * strength ) / 200; + } + } + /* otherwise if the current subpixel is more than threshold, and varies + slightly to left or right, darken it */ + else if ( sp22 > threshold && + sp22 < 255 && + ( leftdiff < 10 || + rightdiff < 10 ) ) + { + if ( sp11 <= 2 && + sp21 <= 2 && + sp31 <= 2 && + sp13 >= threshold && + sp23 >= threshold && + sp33 >= threshold && + prevtotal < nexttotal ) + new_line[xx] += ( ( 255 - new_line[xx] ) * strength ) / 100; + + else if ( sp13 <= 2 && + sp23 <= 2 && + sp33 <= 2 && + nexttotal < prevtotal && + sp11 >= threshold && + sp21 >= threshold && + sp31 >= threshold ) + new_line[xx] += ( ( 255 - new_line[xx] ) * strength ) / 100; + + } + } + } + FT_Bitmap_Copy( library, &new_bitmap, bitmap ); + FT_Bitmap_Done( library, &new_bitmap ); + } + + + /* Grayscale filter */ + static void + _ft_lcd_grayscale_filter ( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_UInt strength, + FT_Library library ) + { + + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* line = bitmap->buffer; + + + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch ) + { + FT_UInt xx; + + + for ( xx = 0; xx < width - 1; xx += 3 ) + { + FT_UInt total = line [xx] + line [xx + 1] + line [xx + 2]; + line[xx] = ( ( 100 - strength ) * line[xx] + + strength * ( total / 3 ) ) / 100; + line[xx+1] = ( ( 100 - strength ) * line[xx+1] + + strength * ( total / 3 ) ) / 100; + line[xx+2] = ( ( 100 - strength ) * line[xx+2] + + strength * ( total / 3 ) ) / 100; + } + } + } + +/* + These need to be in sync with params inside ftinf.c + (not ideal but perhaps better than making these public) + */ +#define STEM_WIDTH_2_PPEM 18 +#define MAX_PPEM 100 + + typedef struct Stem_Segment_ + { + FT_Long x1; + FT_Long x2; + FT_Int y; + } Stem_Segment; + + typedef struct Stem_Center_ + { + FT_Long x; + FT_Long y; + FT_Long w; + FT_Long x1; + FT_Long x2; + } Stem_Center; + + typedef struct Stem_ + { + FT_Long center; + FT_Long count; + FT_Long rcount; /* used to count within a range in possible stems */ + FT_Long width; + FT_Long height; + FT_Short zone; /* 1 2 or 3 */ + FT_Bool generated; + } Stem; + + static void + swap_stem ( Stem* s1, Stem* s2 ) + { + Stem s; + s.center = s1->center; + s.count = s1->count; + s.rcount = s1->rcount; + s.width = s1->width; + s.zone = s1->zone; + s.generated = s1->generated; + + s1->center = s2->center; + s1->count = s2->count; + s1->rcount = s2->rcount; + s1->width = s2->width; + s1->zone = s2->zone; + s1->generated = s2->generated; + + s2->center = s.center; + s2->count = s.count; + s2->rcount = s.rcount; + s2->width = s.width; + s2->zone = s.zone; + s2->generated = s.generated; + } + + /* Stem alignment for bitmaps; A hack with very nice results */ + /* Ideally this could be implemented on the outline, prior to + * rasterization. Possible future enhancement is to use the + * warper code to achieve this */ + static void + _lcd_stem_align ( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_GlyphSlot slot, + FT_Long* translate_value, + float* scale_value, + FT_UInt alignment_strength, + FT_UInt fitting_strength, + float* embolden_value + ) + { + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + + Stem_Segment* segments; + Stem_Segment* leftmost_segment; + Stem_Segment* rightmost_segment; + Stem_Segment* leftmost_segment_not_extrema; + Stem_Segment* rightmost_segment_not_extrema; + Stem* stems; + Stem* possible_stems; + Stem* leftmost_stem; + Stem* rightmost_stem; + Stem_Data* known_stem_values; + Stem_Center* centers; + FT_Long leftmost_point = width * 256; + FT_Long rightmost_point = 0; + FT_Long leftmost_point_not_extrema = width * 256; + FT_Long rightmost_point_not_extrema = 0; + FT_Long num_segments = 0; + FT_Long num_centers = 0; + FT_Long *stem_centers; + FT_UInt h; + FT_ULong valid_stems = 0, valid_possible_stems = 0; + FT_Long center, stem_matches, stem_matches_ledge; + FT_Long stem_matches_redge, next_center, last_matching_center; + FT_Long last_matching_ledge, last_matching_redge, this_center; + FT_Int max_strength; + FT_Byte* line = bitmap->buffer; + FT_UInt current_value = 0; + FT_UInt xx; + FT_Long linearHoriAdvance = slot->linearHoriAdvance >> 10; + + FT_Int m_horiBearingX = slot->metrics.horiBearingX; + FT_Int m_horiAdvance = slot->metrics.horiAdvance; + FT_Int m_width = slot->metrics.width; + FT_Pos one_pixel = 768; + FT_Pos one_third_pixel = 256; + FT_Int columns_per_pixel = 3; + /*FT_Int extra_columns = 6;*/ + + /* on / off flags for testing different features */ + FT_Bool strategy_translate_using_closest_stem = TRUE; + FT_Bool strategy_scale_to_closest_centers = FALSE; + FT_Bool strategy_scale_to_closest_centers_up_only = FALSE; + FT_Bool strategy_always_use_distance_ceiling = FALSE; + FT_Bool strategy_auto_change_center_offset = TRUE; + FT_Bool strategy_use_m_control = FALSE; + FT_Bool strategy_correct_out_of_bounds_outlines = FALSE; + FT_Bool strategy_also_use_edge_detection_for_stems = FALSE; + FT_Bool strategy_use_strengths = TRUE; + FT_Bool strategy_synthesize_stems = FALSE; + FT_Bool strategy_bearing_correction = TRUE; + FT_Bool strategy_use_d_correction = TRUE; + FT_Bool strategy_fit_to_width = FALSE; + /*FT_Bool strategy_center_glyph = FALSE;*/ + + const FT_Int MIN_PPEM = 7; + /*const FT_Int MAX_PPEM = 100;*/ + const FT_Int MAX_STEMS = 3; + FT_Int ppem = 0; + + Stem_Data stem_data; + + /* reset to default */ + *scale_value = 1.0; + + /* Simply return in odd cases where these don't seem to be set */ + /* Flash and some pdf viewers will crash otherwise */ + if ( !slot->face || + !slot->face->size || + !slot->face->size->metrics.x_ppem ) + return; + + if ( slot->face->size->metrics.x_ppem > MAX_PPEM ) + return; + + if ( slot->face->size->metrics.x_ppem < MIN_PPEM ) + return; + + if ( !FT_IS_SCALABLE( slot->face ) ) + return; + + ppem = slot->face->size->metrics.x_ppem; + + if ( ppem < 9 ) + return; + if ( ppem > 20 ) + strategy_use_m_control = TRUE; + + /* only perform alignment on styles we know, that aren't bold or italic */ + /* perhaps detection could be added on those that are not set? */ + /* Require certain ppems for narrow and light fonts */ + if( slot->face->style_name ) + { + if ( strcasestr( slot->face->style_name, "Italic" ) || + strcasestr( slot->face->style_name, "Oblique" ) || + strcasestr( slot->face->style_name, "Script" ) || + strcasestr( slot->face->style_name, "Handwriting" ) || + strcasestr( slot->face->style_name, "Bold" ) || + strcasestr( slot->face->style_name, "Black" ) || + ( ( strcasestr( slot->face->style_name, "Extra Thin" ) || + strcasestr( slot->face->style_name, "Extra Light" ) ) && + ppem < 10 ) || + ( strcasestr( slot->face->style_name, "Thin" ) + && ppem < 10 ) || + ( strcasestr( slot->face->style_name, "Light" ) + && ppem < 10 ) || + ( strcasestr( slot->face->style_name, "Narrow" ) + && ppem < 15 ) || + ( strcasestr( slot->face->style_name, "Condensed" ) + && ppem < 20 ) ) + return; + } + + if( slot->face->family_name ) + { + if ( strcasestr( slot->face->family_name, "Italic" ) || + strcasestr( slot->face->family_name, "Oblique" ) || + strcasestr( slot->face->family_name, "Script" ) || + strcasestr( slot->face->family_name, "Handwriting" ) || + strcasestr( slot->face->family_name, "Bold" ) || + strcasestr( slot->face->family_name, "Black" ) || + ( ( strcasestr( slot->face->family_name, "Extra Thin" ) || + strcasestr( slot->face->family_name, "Extra Light" ) ) && + ppem < 10 ) || + ( strcasestr( slot->face->family_name, "Thin" ) + && ppem < 10 ) || + ( strcasestr( slot->face->family_name, "Light" ) + && ppem < 10 ) || + ( strcasestr( slot->face->family_name, "Narrow" ) + && ppem < 15 ) || + ( strcasestr( slot->face->family_name, "Condensed" ) + && ppem < 20 ) ) + return; + } + else if ( slot->face->style_flags ) + { + if ( slot->face->style_flags & FT_STYLE_FLAG_ITALIC || + slot->face->style_flags & FT_STYLE_FLAG_BOLD || + FT_IS_TRICKY( slot->face ) ) + return; + } + else return; + + if ( mode != FT_RENDER_MODE_LCD ) + { + columns_per_pixel = 1; + one_pixel = 256; + one_third_pixel = 85; + /*extra_columns = 0;*/ + /* until this can be figured out just return */ + /* There are issues with missing glyphs */ + return; + } + + known_stem_values=&stem_data; + if ( ftinf && ftinf->use_known_settings_on_selected_fonts ) + { + ftinf_fill_stem_values( known_stem_values, slot->face->family_name, ppem, TRUE ); + /* translate value may be set for < 10 */ + if (known_stem_values->stem_translating_only > -1024 ) + { + *translate_value = known_stem_values->stem_translating_only; + return; + } + if( known_stem_values->bearing_correction == FALSE ) + strategy_bearing_correction = FALSE; + } else + ftinf_fill_stem_values( known_stem_values, slot->face->family_name, ppem, FALSE ); + + if ( known_stem_values->use_100 || + known_stem_values->m >= 0 ) + { + alignment_strength = fitting_strength = 100; + strategy_use_m_control = TRUE; + } + + if ( known_stem_values->edge_detection ) + strategy_also_use_edge_detection_for_stems = TRUE; + + /* Allocate */ + segments = NULL; + leftmost_segment = (Stem_Segment*) malloc( 4*sizeof ( Stem_Segment ) ); + leftmost_segment_not_extrema = leftmost_segment+1; + rightmost_segment = leftmost_segment+2; + rightmost_segment_not_extrema = leftmost_segment+3; + + stems = (Stem*) malloc ( (2*MAX_STEMS+2) * sizeof ( Stem ) ); + possible_stems = stems+MAX_STEMS; + leftmost_stem = possible_stems+MAX_STEMS; + rightmost_stem = leftmost_stem + 1; + centers = NULL; + + if ( verbose ) + printf("\n"); + + /* Initialize */ + stem_centers=(FT_Long*)calloc( width * 256, sizeof(stem_centers[0]) ); + + rightmost_segment->x1 = 0; + rightmost_segment->x2 = 0; + rightmost_segment->y = 0; + leftmost_segment->x1 = 99999999; + leftmost_segment->x2 = 0; + leftmost_segment->y = 0; + + rightmost_segment_not_extrema->x1 = 0; + rightmost_segment_not_extrema->x2 = 0; + rightmost_segment_not_extrema->y = 0; + leftmost_segment_not_extrema->x1 = 99999999; + leftmost_segment_not_extrema->x2 = 0; + leftmost_segment_not_extrema->y = 0; + + /* Locate stem centers for later processing */ + for ( h = (FT_UInt)bitmap->rows; h > 0; h--, line += bitmap->pitch ) + { + current_value = 0; + /* Calculate various sums and stem widths of glyph */ + for ( xx = 0; xx < width; xx += 1 ) + { + /* Reallocate (in blocks of 64) */ + if( num_segments % 64 == 0 ) + segments = (Stem_Segment*) realloc + ( segments, ( num_segments + 64 ) * sizeof ( Stem_Segment ) ); + + /* if line is white, and now has color, it's the start of a stem */ + if ( current_value == 0 && line[xx] > 0 ) + { + /* start of stem */ + segments[num_segments].x1 = 256 * xx + ( 255 - line[xx] ); + segments[num_segments].y = h; + } + + /* otherwise, if it's currently black and the new value is 0, + it's the end of a stem */ + else if ( ( current_value > 0 && line[xx] == 0 ) || + ( current_value > 0 && xx == width - 1 ) ) + { + FT_Long stem_center_x; + segments[num_segments].x2 = 256 * ( xx - 1 ) + line[xx-1]; + + if ( xx == width - 1 ) + segments[num_segments].x2 += line[xx]; + + /*stem center is average of start and end of stem */ + stem_center_x = ( segments[num_segments].x2 + + segments[num_segments].x1 ) / 2; + + /* Reallocate (in blocks of 32) */ + if( num_centers % 32 == 0 ) + centers = (Stem_Center*) realloc + ( centers, ( num_centers + 32 ) * sizeof ( Stem_Center ) ); + centers[num_centers].x = stem_center_x; + centers[num_centers].y = h; + centers[num_centers].x1 = segments[num_segments].x1; + centers[num_centers].x2 = segments[num_segments].x2; + + num_centers++; + + stem_centers[stem_center_x] += 1; + + /* Find left and rightmost points for later calculations */ + /* OR - Favor ones that aren't on the top or bottom if */ + /* possible to prevent v and w from getting caught later */ + if ( segments[num_segments].x1 < leftmost_segment->x1 || + ( segments[num_segments].y > 1 && + segments[num_segments].y < height && + segments[num_segments].x1 == leftmost_segment->x1 ) ) + { + leftmost_segment->x1 = segments[num_segments].x1; + leftmost_segment->x2 = segments[num_segments].x2; + leftmost_segment->y = h; + } + if ( segments[num_segments].x2 > rightmost_segment->x2 || + ( segments[num_segments].y > 1 && + segments[num_segments].y < height && + segments[num_segments].x1 == rightmost_segment->x1 ) ) + { + rightmost_segment->x1 = segments[num_segments].x1; + rightmost_segment->x2 = segments[num_segments].x2; + rightmost_segment->y = h; + } + + if ( segments[num_segments].x1 + < leftmost_segment_not_extrema->x1 || + ( segments[num_segments].y > 1 && + segments[num_segments].y < height && + segments[num_segments].x1 + == leftmost_segment_not_extrema->x1 && + h < (FT_UInt)bitmap->rows && h > 0 ) ) + { + leftmost_segment_not_extrema->x1 = segments[num_segments].x1; + leftmost_segment_not_extrema->x2 = segments[num_segments].x2; + leftmost_segment_not_extrema->y = h; + } + if ( segments[num_segments].x2 + > rightmost_segment_not_extrema->x2 || + ( segments[num_segments].y > 1 && + segments[num_segments].y < height && + segments[num_segments].x1 + == rightmost_segment_not_extrema->x1 && + h < (FT_UInt)bitmap->rows && h > 0 ) ) + { + rightmost_segment_not_extrema->x1 = segments[num_segments].x1; + rightmost_segment_not_extrema->x2 = segments[num_segments].x2; + rightmost_segment_not_extrema->y = h; + } + + if ( segments[num_segments].x1 < leftmost_point ) + leftmost_point = segments[num_segments].x1; + + if ( segments[num_segments].x2 > rightmost_point ) + rightmost_point = segments[num_segments].x2; + + if ( segments[num_segments].x1 < leftmost_point_not_extrema && + h < (FT_UInt)bitmap->rows && h > 0 ) + leftmost_point_not_extrema = segments[num_segments].x1; + + if ( segments[num_segments].x2 > rightmost_point_not_extrema && + h < (FT_UInt)bitmap->rows && h > 0 ) + rightmost_point_not_extrema = segments[num_segments].x2; + + num_segments++; + } + /* else - other conditions - need some error checking here */ + current_value = line[xx]; + } + } + + /* initialize */ + for ( xx = 0; xx < MAX_STEMS; xx +=1 ) + { + stems[xx].center = 0; + stems[xx].count = 0; + stems[xx].width = 0; + stems[xx].height = 0; + possible_stems[xx].center = 0; + possible_stems[xx].count = 0; + possible_stems[xx].width = 0; + possible_stems[xx].height = 0; + } + + valid_stems = 0; + valid_possible_stems = 0; + + /* Determine which centers belong to stems */ + center = 0; + + while ( center < num_centers ) + { + /* slope at within which to consider a point part of a stem */ + /*const FT_UInt slope = 1; + const FT_UInt topslope = (256 * 3) / 10; */ + + /* 10 to 20 with 4 matches seems good, */ + /* but 1 or 2 with 3 stems needs to somehow get included */ + FT_Int deviation1 = 5; + FT_Int deviation2=-1, requirement1 = 4, stem_match_requirement = 3; + FT_Int center_difference_in_height; + FT_Int center_difference_in_width, valid_center_average; + FT_Int smallest_width_ledge, smallest_width_redge; + FT_Int x1_difference_in_width, x2_difference_in_width; + FT_Bool no_gap_found = FALSE; + FT_Bool no_gap_found_ledge = FALSE; + FT_Bool no_gap_found_redge = FALSE; + FT_Bool stem_detected = FALSE; + FT_Int set_width_to, set_center_to; + + /* seems to not do damage */ + /* May not be effective */ + requirement1 = height / 4; + if ( requirement1 < 5 ) + requirement1 = 5; + deviation1 = 20; + deviation2 = 20; + + if ( columns_per_pixel == 1 ) + deviation1 = deviation2 = 10; + + if ( (FT_Int)bitmap->rows <= 6 ) + deviation1 = 25; + + if ( (FT_Int)bitmap->rows <= 6 ) + deviation2 = 25; + + if ( columns_per_pixel == 1 && + (FT_Int)bitmap->rows <= 6 ) + deviation1 = deviation2 = 12; + + valid_center_average = 0; + + no_gap_found = no_gap_found_ledge = no_gap_found_redge = FALSE; + stem_detected = FALSE; + + if ( ppem < 11 ) + requirement1 = 4; + + if ( ppem > 18 ) + { + stem_match_requirement = height / 4; + if ( stem_match_requirement < 3 ) + stem_match_requirement = 3; + } + + smallest_width_ledge = smallest_width_redge = width * 256; + stem_matches = 0; + stem_matches_ledge = 0; + stem_matches_redge = 0; + last_matching_center = -1; + last_matching_ledge = -1; + last_matching_redge = -1; + + /* set currently looked at center to center value */ + this_center = center; + next_center = 0; + + /* For each center, compare with all other centers to see if others */ + /* match the properties of this one */ + while ( next_center < num_centers ) + { + + /* calculate differences */ + center_difference_in_width = abs ( centers[this_center].x + - centers[next_center].x ); + center_difference_in_height = abs ( centers[this_center].y + - centers[next_center].y ); + x1_difference_in_width = abs ( centers[this_center].x1 + - centers[next_center].x1 ); + x2_difference_in_width = abs ( centers[this_center].x2 + - centers[next_center].x2 ); + + + /* property - stem center points that align */ + /* if the center is within range, the center is less than */ + /* 1/2 the height away, and at least one edge is also within range */ + if ( center_difference_in_width + < center_difference_in_height * deviation1 && + center_difference_in_height + <= (FT_Int)bitmap->rows / 2 && + /* prevents w from getting caught ---- but also kills m */ + ( x1_difference_in_width + < center_difference_in_height * deviation2 || + x2_difference_in_width + < center_difference_in_height * deviation2 ) ) + { + stem_matches += 1; + valid_center_average += centers[next_center].x; + + /* try to find where the matching centers are far apart */ + if ( last_matching_center >= 0 && + abs( centers[last_matching_center].y + - centers[next_center].y ) >= (FT_Int)bitmap->rows / 2 ) + + /* try to find where matching centers are next to each other */ + if ( last_matching_center >= 0 && + abs( centers[last_matching_center].y + - centers[next_center].y ) == 1 ) + no_gap_found = TRUE; + + last_matching_center = next_center; + } + + if ( strategy_also_use_edge_detection_for_stems ) + { + /* property - stem left edge points that align */ + /* if the center is within range, */ + /* the center is less than 1/2 the height away */ + if ( x1_difference_in_width + < center_difference_in_height * deviation1 && + center_difference_in_height <= (FT_Int)bitmap->rows / 2 ) + { + stem_matches_ledge += 1; + /* may not need for edges */ + /*valid_center_average += centers[next_center].x; */ + + if ( centers[next_center].x2 - centers[next_center].x1 + < smallest_width_ledge ) + smallest_width_ledge = centers[next_center].x2 + - centers[next_center].x1; + + /* try to find where the matching centers are far apart */ + if ( last_matching_ledge >= 0 && + abs( centers[last_matching_ledge].y + - centers[next_center].y) + >= (FT_Int)bitmap->rows / 2 ) + + /* try to find where matching centers are next to each other */ + if ( last_matching_ledge >= 0 && + abs( centers[last_matching_ledge].y + - centers[next_center].y ) == 1 ) + no_gap_found_ledge = TRUE; + last_matching_ledge = next_center; + } + } + + if ( strategy_also_use_edge_detection_for_stems ) + { + /* property - stem right edge points that align */ + /* if the center is within range, the center is less than 1/2 */ + /* the height away */ + if ( x2_difference_in_width + < center_difference_in_height * deviation1 && + center_difference_in_height + <= (FT_Int)bitmap->rows / 2 ) + { + stem_matches_redge += 1; + /* may not need for edges */ + /*valid_center_average += centers[next_center].x; */ + + if ( centers[next_center].x2 - centers[next_center].x1 + < smallest_width_redge ) + smallest_width_redge = centers[next_center].x2 + - centers[next_center].x1; + + /* try to find where the matching centers are far apart */ + if ( last_matching_redge >= 0 && + abs( centers[last_matching_redge].y + - centers[next_center].y ) >= (FT_Int)bitmap->rows / 2 ) + + /* try to find where matching centers are next to each other */ + if ( last_matching_redge >= 0 && + abs( centers[last_matching_redge].y + - centers[next_center].y ) == 1 ) + no_gap_found_redge = TRUE; + + last_matching_redge = next_center; + } + } + + next_center++; + } + + if ( stem_matches > 0 ) + valid_center_average /= stem_matches; + + if ( ( stem_matches >= stem_match_requirement || + ( ( (FT_Int)bitmap->rows <= 6 || ppem < 11 ) && + stem_matches >= 2 && + abs ( valid_center_average + - centers[center].x) < deviation1 /2 ) || + /* try to catch tightly aligned stuff where the matching centers */ + /* are next to each other only */ + ( stem_matches == 2 && + abs( valid_center_average + - centers[center].x) <= deviation1 /2 && + no_gap_found && + ppem < 18 ) ) && + /* catches things like times 16 u but gets a lot of w's too */ + /* stem width is less than 1/3 of the bitmap width, */ + /* or bitmap_width is small */ + ( centers[center].x2 - centers[center].x1 + < (m_horiAdvance * 12) / 2 || + m_horiAdvance * 12 <= columns_per_pixel * one_pixel ) ) + { + stem_detected = TRUE; + set_width_to = centers[center].x2 - centers[center].x1; + set_center_to = centers[center].x; + } + + /* see if edges found anything */ + if ( strategy_also_use_edge_detection_for_stems && !stem_detected ) + { + /* Require no gap for edges */ + /* stem width less than 1/3 bitmap width, or bitmap_width is small */ + /* The stem occurs on the left side of glyph only */ + if ( ( stem_matches_ledge >= stem_match_requirement && + no_gap_found_ledge ) && + ( centers[center].x2 - centers[center].x1 + < ( m_horiAdvance * 12 ) / 2 || + m_horiAdvance * 12 <= columns_per_pixel * one_pixel ) && + centers[center].x < ( m_horiAdvance * 12 ) / 2 ) + { + stem_detected = TRUE; + set_width_to = smallest_width_ledge; + set_center_to = centers[center].x1 + set_width_to / 2; + stem_matches = stem_matches_ledge; + } + /* Require no gap for edges */ + /* stem width is less than 1/3 bitmap width, or bitmap_width is small */ + /* The stem occurs on the right side of glyph only */ + else if ( ( stem_matches_redge >= stem_match_requirement && + no_gap_found_redge ) && + ( centers[center].x2 - centers[center].x1 + < ( m_horiAdvance * 12 ) / 2 || + m_horiAdvance * 12 <= columns_per_pixel * one_pixel ) && + centers[center].x > (m_horiAdvance * 12) / 2 ) + { + stem_detected = TRUE; + set_width_to = smallest_width_redge; + set_center_to = centers[center].x2 - set_width_to / 2; + stem_matches = stem_matches_redge; + } + } + + + /*store and/or replace highest occurrences with 3 or more centers */ + /* because this matched, it will become the top dog regardless */ + if ( stem_detected && (stem_matches > possible_stems[0].height) ) + { + /* if this is the first stem just go ahead */ + if ( valid_possible_stems == 0 ) + { + valid_possible_stems = 1; + possible_stems[0].center = set_center_to; + possible_stems[0].count = stem_matches; + possible_stems[0].width = set_width_to; + possible_stems[0].height = stem_matches; + } + + /* otherwise, if there is already a stem */ + else if ( valid_possible_stems == 1 ) + { + /* if stem is within range of existing one, replace existing one */ + + /* if the stem isn't within the range of this one swap it with */ + /* next one first */ + if ( abs ( set_center_to - possible_stems[0].center ) + >= one_pixel * 2 ) + { + swap_stem ( &possible_stems[0], &possible_stems[1] ); + valid_possible_stems = 2; + } + possible_stems[0].center = set_center_to; + possible_stems[0].count = stem_matches; + possible_stems[0].width = set_width_to; + possible_stems[0].height = stem_matches; + } + + /* otherwise if there are already 2 stems */ + else if ( valid_possible_stems >= 2 ) + { + /* if the stem is within the range of existing one, replace */ + /* existing one */ + if ( abs ( set_center_to - possible_stems[0].center ) + <= one_pixel * 2 ) + { + possible_stems[0].center = set_center_to; + possible_stems[0].count = stem_matches; + possible_stems[0].width = set_width_to; + possible_stems[0].height = stem_matches; + } + /* if the stem isn't within the range of this one */ + else + { + /* see if within range of next one and swap if so and proceed */ + /* overwriting it */ + if ( abs ( set_center_to - possible_stems[1].center ) + <= one_pixel * 2 ) + swap_stem ( &possible_stems[0], &possible_stems[1] ); + + /* otherwise see if in range of third one */ + else if ( abs ( set_center_to - possible_stems[2].center ) + <= one_pixel * 2 ) + swap_stem ( &possible_stems[0], &possible_stems[2] ); + + /* otherwise this is the new top dog, so demote everything */ + else + { + swap_stem ( &possible_stems[1], &possible_stems[2] ); + swap_stem ( &possible_stems[0], &possible_stems[1] ); + valid_possible_stems += 1; + } + possible_stems[0].center = set_center_to; + possible_stems[0].count = stem_matches; + possible_stems[0].width = set_width_to; + possible_stems[0].height = stem_matches; + } + } + } + + else if ( stem_matches > possible_stems[1].height && + set_center_to != 0 ) + { + + /* make sure it doesn't match the first stem */ + if ( abs ( set_center_to - possible_stems[0].center ) >= one_pixel * 2 ) + { + + /* if this is the second stem */ + if ( valid_possible_stems == 1 ) + valid_possible_stems = 2; + + /* otherwise if there is already a stem here */ + else if ( valid_possible_stems >= 2 ) + { + /* if it doesn't match the second stem, proceed to swap out */ + /* with the third. if it does, replace it */ + if ( abs ( set_center_to - possible_stems[1].center ) + >= one_pixel * 2 ) + { + swap_stem ( &possible_stems[1], &possible_stems[2] ); + valid_possible_stems +=1; + } + } + possible_stems[1].center = set_center_to; + possible_stems[1].count = stem_matches; + possible_stems[1].width = set_width_to; + possible_stems[1].height = stem_matches; + } + } + + else if ( stem_matches > possible_stems[2].height && + set_center_to != 0 ) + { + /* if it doesn't match the first or second one */ + if ( abs( set_center_to - possible_stems[0].center) >= one_pixel * 2 && + abs( set_center_to - possible_stems[1].center) >= one_pixel * 2 ) + { + if ( valid_possible_stems == 2 ) + valid_possible_stems += 1; + + possible_stems[2].center = set_center_to; + possible_stems[2].count = stem_matches; + possible_stems[2].width = set_width_to; + possible_stems[1].height = stem_matches; + } + } + + if ( valid_possible_stems > 3 ) + valid_possible_stems = 3; + + center++; + } + + /* promote to stem */ + if ( valid_possible_stems > 0 ) + { + stems[0].center = possible_stems[0].center; + stems[0].count = possible_stems[0].count; + stems[0].width = possible_stems[0].width; + stems[0].height = possible_stems[0].height; + stems[0].generated = FALSE; + valid_stems++; + } + + if ( valid_stems == 1 && + valid_possible_stems > 1 ) + { + stems[1].center = possible_stems[1].center; + stems[1].count = possible_stems[1].count; + stems[1].width = possible_stems[1].width; + stems[1].height = possible_stems[1].height; + stems[1].generated = FALSE; + valid_stems++; + } + + if ( valid_stems == 2 && + valid_possible_stems > 2 && + possible_stems[2].center != 0 ) + { + stems[2].center = possible_stems[2].center; + stems[2].count = possible_stems[2].count; + stems[2].width = possible_stems[2].width; + stems[2].height = possible_stems[2].height; + stems[2].generated = FALSE; + valid_stems++; + } + + /* sort stems in x direction */ + if ( valid_stems == 3 ) + { + if ( stems[0].center > stems[1].center ) + swap_stem ( &stems[0], &stems[1] ); + + if ( stems[0].center > stems[2].center ) + swap_stem ( &stems[1], &stems[2] ); + + if ( stems[1].center > stems[2].center ) + swap_stem ( &stems[1], &stems[2] ); + + if ( stems[0].center > stems[1].center ) + swap_stem ( &stems[0], &stems[1] ); + + /* only look at first and last stem for now */ + swap_stem ( &stems[1], &stems[2] ); + } + + /* synthesize stems - Works, but needs work */ + if ( ( strategy_synthesize_stems || + known_stem_values->synth_stems ) && + valid_stems == 0 && + ppem > 10 ) + { + /* if the leftmost segment's leftmost point is the same as the glyph's */ + /* leftmost point, and it is of reasonable width, and is not on the */ + /* top or bottom of the bitmap */ + if ( leftmost_segment_not_extrema->x1 + == leftmost_point_not_extrema && + abs ( leftmost_segment_not_extrema->x2 + - leftmost_segment_not_extrema->x1 ) + < ( rightmost_point_not_extrema + - leftmost_point_not_extrema ) / 3 && + leftmost_segment_not_extrema->y < height && + leftmost_segment_not_extrema->y > 1 ) + { + stems[valid_stems].center = ( leftmost_segment_not_extrema->x2 + + leftmost_segment_not_extrema->x1 ) / 2; + stems[valid_stems].width = leftmost_segment_not_extrema->x2 + - leftmost_segment_not_extrema->x1; + stems[valid_stems].generated = TRUE; + valid_stems += 1; + } + + + if ( rightmost_segment_not_extrema->x2 + == rightmost_point_not_extrema && + abs ( rightmost_segment_not_extrema->x2 + - rightmost_segment_not_extrema->x1 ) + < ( rightmost_point_not_extrema + - leftmost_point_not_extrema ) / 3 && + rightmost_segment_not_extrema->y < height && + rightmost_segment_not_extrema->y > 1 ) + { + stems[valid_stems].center = ( rightmost_segment_not_extrema->x2 + + rightmost_segment_not_extrema->x1 ) / 2; + stems[valid_stems].width = rightmost_segment_not_extrema->x2 + - rightmost_segment_not_extrema->x1; + stems[valid_stems].generated = TRUE; + valid_stems += 1; + } + + } + + /* sort stems in x direction */ + if ( valid_stems > 1 && stems[0].center > stems[1].center ) + swap_stem ( &stems[0], &stems[1] ); - return 0; - } + if ( valid_stems == 0 && known_stem_values->stem_translating != 0 ) + { + *translate_value += known_stem_values->stem_translating; + if ( strategy_use_strengths ) + { + /* consider 1/2 pixel the max when strength is at 100%, + unless translate is already greater than that */ + FT_Int strength_cutoff = 32; - /* sets render-specific mode */ - static FT_Error - ft_smooth_set_mode( FT_Renderer render, - FT_ULong mode_tag, - FT_Pointer data ) - { - /* we simply pass it to the raster */ - return render->clazz->raster_class->raster_set_mode( render->raster, - mode_tag, - data ); - } - /* transform a given glyph image */ - static FT_Error - ft_smooth_transform( FT_Renderer render, - FT_GlyphSlot slot, - const FT_Matrix* matrix, - const FT_Vector* delta ) - { - FT_Error error = FT_Err_Ok; + if ( abs ( *translate_value ) > strength_cutoff) + strength_cutoff = *translate_value; + max_strength = ( strength_cutoff * alignment_strength ) / 100; - if ( slot->format != render->glyph_format ) + if ( *translate_value < -max_strength ) + *translate_value = -max_strength; + else if ( *translate_value > max_strength ) + *translate_value = max_strength; + } + } + else + /* Start snapping */ { - error = FT_THROW( Invalid_Argument ); + FT_Int center_offset; + FT_Int modulus; + FT_Int delta, delta2; + FT_Long stem_distance = 1, new_distance = 1; + FT_Int distance_floor, distance_ceiling; + FT_Int translate_value2 = 0; + FT_Int main_stem = 0; + FT_Int lbearing = m_horiBearingX * 12; + FT_Int bitmap_stem_location = stems[0].center; + FT_Int advance_stem_location = bitmap_stem_location + + lbearing - one_pixel; + FT_Int advance_width = m_horiAdvance * 12; + FT_Int original_advance_width = 12 * ( slot->linearHoriAdvance >> 10 ); + FT_Int glyph_width = rightmost_point - leftmost_point; + FT_Int stem_width = stems[0].width; + FT_Int advance_leftmost_location = leftmost_point + + lbearing - one_pixel; + FT_Int advance_rightmost_location = rightmost_point + + lbearing - one_pixel; + +#define proposed_transformed_point(point) \ + point * (float)(new_distance) / (float)(stem_distance) \ + + *translate_value * 12 - ( stems[main_stem].center * (float)(new_distance) \ + / (float)(stem_distance) - stems[main_stem].center) + +#define proposed_translated_point(point) point + *translate_value * 12 + + center_offset = one_pixel / 2; /* half pixel */ + modulus = one_pixel; /* whole pixel */ + + /* Determine center_offset via known values */ + if ( known_stem_values->stem_width >= 0 ) + { + if ( known_stem_values->stem_width % 2 == 0 ) + center_offset = 0; + else + center_offset = one_pixel / 2; + } + /* otherwise do intelligent guessing, if set */ + else if ( strategy_auto_change_center_offset && + ppem >= STEM_WIDTH_2_PPEM && + stems[0].width < one_pixel * 1.45 ) + center_offset = one_pixel / 2; + else if ( strategy_auto_change_center_offset && + ppem >= STEM_WIDTH_2_PPEM && + stems[0].width >= one_pixel * 1.45 && + stems[0].width < one_pixel * 2.6 ) + center_offset = 0; + else if ( strategy_auto_change_center_offset && + ppem >= STEM_WIDTH_2_PPEM && + stems[0].width >= one_pixel * 2.6 && + stems[0].width < one_pixel * 3.6 ) + center_offset = one_pixel / 2; + else if ( strategy_auto_change_center_offset && + ppem >= STEM_WIDTH_2_PPEM ) + center_offset = + ( one_pixel + * ( ( ( (int)( stems[0].width + one_pixel / 2 ) ) + / one_pixel ) % 2 ) ) / 2; + + /* Snap to closest translate and scale values by default */ + if ( valid_stems >= 1 ) + { + /* closest snapping point for stem 0 */ + delta = ( stems[0].center + center_offset ) % modulus; + + if ( delta < modulus / 2 ) + /* snap left */ + *translate_value = -delta / ( columns_per_pixel * 4 ); + else + /* snap right */ + *translate_value = ( modulus - delta ) / ( columns_per_pixel * 4 ); + } + + if ( strategy_use_d_correction ) + { + /* if the only stem is in the last 1/3 of glyph width, the advance */ + /* is 6 pixels, the ppem 11, and doing so doesn't violate bitmap , */ + /* boundaries force it to snap right */ + if ( valid_stems == 1 && + advance_stem_location > (advance_width * 2) / 3 && + advance_width == 6 * one_pixel && + rightmost_point + modulus - delta + <= ( width - (columns_per_pixel * 2) / 3) * 256 && + ppem == 11 ) + *translate_value = ( modulus - delta ) / ( columns_per_pixel * 4 ); + } + + if ( strategy_use_strengths ) + { + /* consider 1/2 pixel the max when strength is at 100%, + unless translate is already greater than that */ + FT_Int strength_cutoff = 32; + if ( abs ( *translate_value ) > strength_cutoff ) + strength_cutoff = *translate_value; + + max_strength = ( strength_cutoff * alignment_strength ) / 100; + + if ( *translate_value < -max_strength ) + *translate_value = -max_strength; + else if ( *translate_value > max_strength ) + *translate_value = max_strength; + } + + /* If 2 stems is detected, scale distance + between in order to land on pixels */ + if ( valid_stems >= 2 ) + { + stem_distance = abs ( stems[1].center - stems[0].center ); + + delta = stem_distance % modulus; + new_distance = stem_distance - delta; + + distance_floor = stem_distance - delta; + distance_ceiling = stem_distance + ( modulus - delta ); + + if ( delta < modulus / 2 ) + new_distance = distance_floor; + else + new_distance = distance_ceiling; + + if ( columns_per_pixel == 3 && + valid_stems == 3 && + strategy_use_m_control && + ( width - 2 * columns_per_pixel ) > 6 * columns_per_pixel && + ppem > 8 && + ( advance_stem_location - advance_leftmost_location ) + < stems[main_stem].width * 2 ) + { + /* Possibly use 2 only when compatible widths is on? */ + FT_Int mod_factor = 2; + + if ( verbose ) + printf ( "USING M CONTROL "); + + distance_floor = stem_distance + - stem_distance % ( modulus * mod_factor ) ; + distance_ceiling = distance_floor + modulus * mod_factor; + + new_distance = distance_ceiling; + + /* force certain ideal situations */ + /* these 2 are mostly safe to do */ + if ( distance_ceiling + + one_pixel * columns_per_pixel == advance_width && + stem_width < one_pixel * 1.25 ) + new_distance = distance_ceiling; + /* NEED TO FIGURE OUT A WAY TO DETERMINE WHETHER + THAT NUDGE IS UP OR DOWN */ + else if ( stem_distance + one_pixel * 2.6 >= advance_width && + stem_width < one_pixel * 1.25 ) + new_distance = distance_ceiling; + + if ( proposed_transformed_point ( leftmost_point ) + < one_third_pixel * 2 || + proposed_transformed_point ( rightmost_point ) + > ( width -2 ) * one_third_pixel ) + new_distance = distance_floor; + + /* NEED TO IGNORE SERIF Ms HERE */ + /* perhaps check bitmap boundaries instead??? */ + if ( strategy_bearing_correction && new_distance == distance_ceiling ) + { + /* Correct if bearings are made substantially worse + (more than 1/3 a pixel beyond advance) */ + if ( proposed_transformed_point( advance_rightmost_location ) + > advance_width + one_third_pixel && + proposed_transformed_point( advance_rightmost_location ) + > advance_rightmost_location && + -proposed_transformed_point( advance_leftmost_location ) + < advance_rightmost_location - advance_width ) + new_distance = distance_floor; + } + + if ( known_stem_values->m >= 0 ) + { + if ( known_stem_values->m == 0 ) + new_distance = distance_floor; + else + new_distance = distance_ceiling; + } + + if ( ( rightmost_point - leftmost_point) - + ( ( rightmost_point * *scale_value) + - ( leftmost_point * *scale_value ) ) >= one_pixel * 1.5 ) + { + *scale_value = 1.0; + *translate_value = 0; + goto Exit; + } + + } + else if ( columns_per_pixel == 1 && + valid_stems == 3 && + strategy_use_m_control && valid_stems == 3 && + width >= 6 * columns_per_pixel && + ppem > 8 && + ( advance_stem_location - advance_leftmost_location ) + < stems[main_stem].width * 2 ) + { + /* Possibly use 2 only when compatible widths is on? */ + FT_Int mod_factor = 2; + + if ( verbose ) + printf ("USING M CONTROL "); + distance_floor = stem_distance - stem_distance + % ( modulus * mod_factor) ; + distance_ceiling = distance_floor + modulus * mod_factor; + + new_distance = distance_ceiling; + + /* force certain ideal situations */ + /* these 2 are mostly safe to do */ + if ( distance_ceiling + + one_pixel * columns_per_pixel == advance_width && + stem_width < one_pixel * 1.25 ) + new_distance = distance_ceiling; + /* NEED TO FIGURE OUT A WAY TO DETERMINE WHETHER + THAT NUDGE IS UP OR DOWN */ + else if ( stem_distance + one_pixel * 2.6 >= advance_width && + stem_width < one_pixel * 1.25 ) + new_distance = distance_ceiling; + + if ( proposed_transformed_point( leftmost_point ) < 0 || + proposed_transformed_point( rightmost_point ) + > width * one_pixel - 2 * one_third_pixel ) + new_distance = distance_floor; + + /* NEED TO IGNORE SERIF Ms HERE */ + /* perhaps check bitmap boundaries instead??? */ + if ( strategy_bearing_correction && new_distance == distance_ceiling ) + { + /* Correct if bearings are made substantially worse + (more than 1/3 a pixel beyond advance) */ + if ( proposed_transformed_point( advance_rightmost_location ) + > advance_width + one_third_pixel && + proposed_transformed_point( advance_rightmost_location ) + > advance_rightmost_location && + -proposed_transformed_point( advance_leftmost_location ) + < advance_rightmost_location - advance_width ) + new_distance = distance_floor; + } + + if ( known_stem_values->m >= 0 ) + { + if ( known_stem_values->m == 0 ) + new_distance = distance_floor; + else + new_distance = distance_ceiling; + } + + + if ( ( rightmost_point - leftmost_point ) + - ( ( rightmost_point * *scale_value ) + - ( leftmost_point * *scale_value ) ) >= one_pixel * 1.5 ) + { + *scale_value = 1.0; + *translate_value = 0; + goto Exit; + } + + } + else + { + if ( strategy_fit_to_width ) + new_distance = advance_width - 3 * one_pixel; + else if ( known_stem_values->stem_scaling >= 0 ) + { + if ( known_stem_values->stem_scaling > 0 ) + new_distance = distance_ceiling; + else + new_distance = distance_floor; + + /* enforce advance width boundaries */ + /* TOO RESTRICTIVE ON SERIF FONTS */ + if ( proposed_transformed_point( advance_rightmost_location ) + >= advance_width || + proposed_transformed_point( advance_leftmost_location ) + <= 0 ) + new_distance = distance_floor; + + /* enforce literal bitmap boundaries if no translate room */ + if ( ( proposed_transformed_point(rightmost_point) >= width * 256 + || proposed_transformed_point(leftmost_point ) <= one_pixel ) + && new_distance + one_pixel * 3 > advance_width ) + new_distance = distance_floor; + + } + else if ( strategy_translate_using_closest_stem ) + { + /* closest snapping point for stem 1 */ + delta2 = ( stems[1].center + center_offset ) % modulus; + + if ( delta2 < modulus / 2 ) + /* snap left */ + translate_value2 = -delta2 / ( columns_per_pixel * 4 ); + else + /* snap right */ + translate_value2 = ( modulus - delta2 ) + / ( columns_per_pixel * 4 ); + + if ( abs ( translate_value2 ) < abs ( *translate_value ) ) + { + *translate_value = translate_value2; + main_stem = 1; + } + + } + else if ( strategy_scale_to_closest_centers ) + { + /* closest snapping point for stem 0 */ + delta = ( stems[0].center + center_offset ) % modulus; + delta2 = ( stems[1].center + center_offset ) % modulus; + + if ( delta < modulus / 2 ) + /* stretch left */ + new_distance = delta + stem_distance; + else + /* stretch right */ + new_distance = delta - modulus + stem_distance; + + if ( delta2 < modulus / 2 ) + new_distance -= delta2; /* stretch left */ + else + new_distance += modulus - delta2; /* stretch right */ + + } + else if ( strategy_scale_to_closest_centers_up_only ) + { + FT_Int net_change = 0; + + /* closest snapping point for stem 0 */ + delta = ( stems[0].center + center_offset ) % modulus; + delta2 = ( stems[1].center + center_offset ) % modulus; + + if ( delta < modulus / 2 ) + net_change = delta; /* stretch left */ + else + net_change = -( modulus - delta ); /* stretch right */ + + if ( delta2 < modulus / 2 ) + net_change -= delta2; /* stretch left */ + else + net_change += modulus - delta2; /* stretch right */ + + if ( net_change > 0 && + proposed_transformed_point( advance_rightmost_location ) + < advance_width && + proposed_transformed_point( advance_leftmost_location ) > 0 ) + new_distance = distance_ceiling; + } + + else if ( strategy_always_use_distance_ceiling ) + { + if ( proposed_transformed_point( advance_rightmost_location ) + < advance_width && + proposed_transformed_point( advance_leftmost_location ) > 0 ) + new_distance = distance_ceiling; + } + } + + if ( strategy_use_strengths ) + { + FT_Int strength_cutoff = center_offset; + + + delta2 = new_distance - stem_distance; + + if ( abs ( delta2 ) > strength_cutoff ) + strength_cutoff = delta2; + + max_strength = ( strength_cutoff * fitting_strength ) / 100; + + if ( delta2 < -max_strength ) + new_distance = stem_distance - max_strength; + else if ( delta2 > max_strength ) + new_distance = stem_distance + max_strength; + } + + *scale_value = (float)( new_distance ) / (float)( stem_distance ); + *translate_value = *translate_value + - ( (float)( stems[main_stem].center * (float)new_distance ) + / (float)stem_distance - stems[main_stem].center ) / 12; + + if ( valid_stems == 2 ) + *embolden_value = ( 64.0 / *scale_value - 64.0 ); + + if ( valid_stems == 3 ) + *embolden_value = ( 64.0 / *scale_value - 64.0 ) / 1.5; + } + + if ( verbose ) + printf ( "%lu stems:", valid_stems ); + + if ( valid_stems == 1 && verbose ) + printf ( "1 stem: bitmapwidth:%d glyphwidth:%f glyph_width:%f center:%f bearing:%f advance:%f lhadvance:%f stemwidth:%f %d %d", + (width - 6) / columns_per_pixel, + (float)m_width / 64.0, + (float)glyph_width / (float)one_pixel, + (float)( (float)advance_stem_location ) / (float)one_pixel, + (float)m_horiBearingX / 64.0, + (float)m_horiAdvance / 64.0, + (float)linearHoriAdvance / 64.0, + (float)stems[0].width / (float)one_pixel, + advance_width, original_advance_width ); + else if ( valid_stems >= 2 && verbose ) + printf ( "%lu stems: bitmapwidth:%d center1:%f center2:%f difference:%f bearing:%f advance:%f advstemloc:%f ", + valid_stems, + (width - 6) / columns_per_pixel, + ( (float)advance_stem_location ) / (float)one_pixel, + ( (float)advance_stem_location + + (float)abs ( stems[1].center + - stems[0].center) ) / (float)one_pixel, + ( (float)abs ( stems[1].center + - stems[0].center ) ) / (float)one_pixel, + (float)m_horiBearingX / 64.0, + (float)m_horiAdvance / 64.0, + (float)advance_stem_location / (float)one_pixel ); + + if ( strategy_bearing_correction ) + { + /* Correct if negative bearings are made substantially worse */ + /* (more than 1/3 a pixel) */ + if ( proposed_transformed_point( advance_rightmost_location ) + > advance_width && + proposed_transformed_point( advance_rightmost_location ) + > advance_rightmost_location && + -proposed_transformed_point( advance_leftmost_location ) + < advance_rightmost_location - advance_width && + *translate_value + > one_third_pixel / ( columns_per_pixel * 4 ) ) + { + *translate_value -=64 ; + if ( verbose ) + printf ( "TRANSLATING -64 " ); + } + } goto Exit; } - if ( matrix ) - FT_Outline_Transform( &slot->outline, matrix ); + Exit: - if ( delta ) - FT_Outline_Translate( &slot->outline, delta->x, delta->y ); +#define transformed_point( point ) point * *scale_value + *translate_value * 12 - Exit: - return error; + if ( strategy_correct_out_of_bounds_outlines ) + { + /* Correct if outside bitmap */ + if ( transformed_point( rightmost_point ) + >= width * 256 - 2 * one_third_pixel && + transformed_point( leftmost_point ) + > one_pixel + 2 * one_third_pixel ) + *translate_value -=64 ; + else if ( transformed_point( leftmost_point ) + <= one_pixel / 2 && + transformed_point( rightmost_point ) + <= width * 256 - ( one_pixel + one_pixel / 2 ) ) + *translate_value += 64; + } + + STVALUES + free ( centers ); + free ( segments ); + free ( stem_centers ); + free ( stems ); + free ( leftmost_segment ); } - /* return the glyph's control box */ + /* Gamma correction */ static void - ft_smooth_get_cbox( FT_Renderer render, - FT_GlyphSlot slot, - FT_BBox* cbox ) + _ft_lcd_gamma_correction_correction ( FT_Bitmap* bitmap, + FT_Render_Mode mode, + FT_GlyphSlot slot, + float gamma_correction_lt, + float gamma_correction_value ) { - FT_MEM_ZERO( cbox, sizeof ( *cbox ) ); + if ( gamma_correction_value != 1.0 ) + { + FT_UInt width = (FT_UInt)bitmap->width; + FT_UInt height = (FT_UInt)bitmap->rows; + FT_Byte* line = bitmap->buffer; + float ppem = (float)slot->face->size->metrics.x_ppem; - if ( slot->format == render->glyph_format ) - FT_Outline_Get_CBox( &slot->outline, cbox ); + + if ( !slot->face || !slot->face->size ) return; + + if ( ppem >= 5 ) + for ( height = (FT_UInt)bitmap->rows; + height > 0; + height--, line += bitmap->pitch ) + { + FT_UInt xx; + + + for ( xx = 0; xx < width; xx += 1 ) + { + /*normal*/ + /*line[xx] = pseudo_gamma ( line[xx], gamma_correction_value );*/ + + /* sloped */ + /*line[xx] = pseudo_gamma ( line[xx], gamma_correction_value - 5 + * (1-gamma_correction_value)/(gamma_correction_lt -5) + + ((1-gamma_correction_value)/(gamma_correction_lt -5)) * ppem );*/ + + /* 1/3-sloped */ + line[xx] = pseudo_gamma ( line[xx], gamma_correction_value - 5 + * ( ( 1 - gamma_correction_value ) + / ( 3 * ( gamma_correction_lt -5 ) ) ) + + ( ( 1 - gamma_correction_value ) + / ( 3 * ( gamma_correction_lt -5) ) ) * ppem ); + } + } + } } +#endif /* convert a slot's glyph image into a bitmap */ static FT_Error @@ -103,25 +2328,162 @@ FT_Render_Mode required_mode ) { FT_Error error; - FT_Outline* outline = &slot->outline; - FT_Bitmap* bitmap = &slot->bitmap; - FT_Memory memory = render->root.memory; + FT_Outline* outline = NULL; + FT_Outline* outline_orig = NULL; FT_BBox cbox; - FT_Pos x_shift = 0; - FT_Pos y_shift = 0; - FT_Pos x_left, y_top; - FT_Pos width, height, pitch; + FT_Pos width, height, pitch, ppem; #ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING FT_Pos height_org, width_org; #endif + FT_Bitmap* bitmap = &slot->bitmap; + FT_Memory memory = render->root.memory; FT_Int hmul = mode == FT_RENDER_MODE_LCD; FT_Int vmul = mode == FT_RENDER_MODE_LCD_V; + FT_Pos x_shift = 0; + FT_Pos y_shift = 0; + FT_Pos x_left, y_top; FT_Raster_Params params; - FT_Bool have_outline_shifted = FALSE; - FT_Bool have_buffer = FALSE; + FT_Bool have_translated_origin = FALSE; + FT_Bool have_outline_shifted = FALSE; + FT_Bool have_buffer = FALSE; + +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + FT_Matrix scaleMat; + FT_Long translate_value = 0; + float scale_value = 1.0; + FT_Int align_called = 0; + + + int chromeos_style_sharpening_strength = 0; + int alignment_strength = 0; + int fitting_strength = 0; + int fringe_filter_strength = 0; + int grayscale_filter_strength = 0; + + int autohint_horizontal_stem_darken_strength = 0; + int autohint_vertical_stem_darken_strength = 0; + + int windows_style_sharpening_strength = 0; + float gamma_correction_value = 1; + float gamma_correction_lt = 0; + + FT_Int brightness_value = 0.0; + FT_Int contrast_value = 0.0; + + FT_Int snapping_sliding_scale_value = 0; + + FT_Int global_embolden_x_value = 0; + FT_Int global_embolden_y_value = 0; + + FT_Int bold_embolden_x_value = 0; + FT_Int bold_embolden_y_value = 0; + + FT_Byte chromeos_cutoff; + double chromeos_gamma_value; + + float embolden_value = 0.0; + FT_Bool autohinted = FALSE; + FT_Bool use_various_tweaks = FALSE; + FT_Pos cur_width = infinality_cur_width; + + const FT_Int MIN_PPEM = 1; + /*const FT_Int MAX_PPEM = 100; */ + + FT_Bool use_known_settings_on_selected_fonts; + + if ( slot->face && + slot->face->size && + slot->face->size->metrics.x_ppem ) + ppem = slot->face->size->metrics.x_ppem; + else + ppem = 0; + + if ( cur_width ) + { + autohinted = TRUE; + } + if( ftinf ){ + const float *f=ftinf->gamma_correction; + + use_known_settings_on_selected_fonts=ftinf->use_known_settings_on_selected_fonts; + use_various_tweaks=ftinf->use_various_tweaks; + snapping_sliding_scale_value=ftinf->stem_snapping_sliding_scale; + + alignment_strength=ftinf->stem_alignment_strength; + if ( snapping_sliding_scale_value != 0 ) + alignment_strength = sliding_scale(10, snapping_sliding_scale_value, alignment_strength, 100, ppem); + + fitting_strength=ftinf->stem_fitting_strength; + if ( snapping_sliding_scale_value != 0 ) + fitting_strength = sliding_scale(10, snapping_sliding_scale_value, fitting_strength, 100, ppem); + + chromeos_style_sharpening_strength=ftinf->chromeos_style_sharpening_strength; + + if ( ppem > 10 ) + chromeos_style_sharpening_strength = + ( chromeos_style_sharpening_strength * ppem ) / 10; + + if ( chromeos_style_sharpening_strength > 100 ) + chromeos_style_sharpening_strength = 100; + + brightness_value=ftinf->brightness; + contrast_value=ftinf->contrast; + windows_style_sharpening_strength=ftinf->windows_style_sharpening_strength; + + /* Decrease effect slightly to have a more linear increase in sharpness */ + windows_style_sharpening_strength = + ( ( windows_style_sharpening_strength + * windows_style_sharpening_strength ) / 100 + + windows_style_sharpening_strength ) / 2; + + gamma_correction_lt = f[0]; + gamma_correction_value = f[1] / 100.0f; + + fringe_filter_strength=ftinf->fringe_filter_strength; + grayscale_filter_strength=ftinf->grayscale_filter_strength; + + autohint_horizontal_stem_darken_strength=ftinf->autohint_horizontal_stem_darken_strength; + autohint_vertical_stem_darken_strength=ftinf->autohint_vertical_stem_darken_strength; + + global_embolden_x_value=ftinf->global_embolden_x_value; + global_embolden_y_value=ftinf->global_embolden_y_value; + + bold_embolden_x_value=ftinf->bold_embolden_x_value; + bold_embolden_y_value=ftinf->bold_embolden_y_value; + } else { + use_known_settings_on_selected_fonts=FALSE; + } + + /* set gamma value to 1 if out of range */ + if ( slot->face && + slot->face->size && + slot->face->size->metrics.x_ppem ) + { + if ( slot->face->size->metrics.x_ppem >= gamma_correction_lt ) + gamma_correction_value = 1; + } + else + gamma_correction_value = 1; + + if( use_various_tweaks && + slot->face && + slot->face->style_name ) + { + /* needs to also check for artifical italics */ + if ( strcasestr(slot->face->style_name, "Italic" ) || + strcasestr(slot->face->style_name, "Oblique" ) ) + { + windows_style_sharpening_strength = 0; + chromeos_style_sharpening_strength = 0; + } + } + + /*if (fitting_strength == 100) scale_value = 1.1;*/ + +#endif /* check glyph image format */ if ( slot->format != render->glyph_format ) @@ -137,26 +2499,119 @@ goto Exit; } +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET +RERENDER: + if ( align_called == 1 ) + { + scaleMat.xx = FT_FixedFromFloat(scale_value); + scaleMat.xy = 0; + scaleMat.yx = 0; + scaleMat.yy = ( 1 << 16 ); + + FT_Outline_Copy(outline_orig, outline); + + if ( scale_value != 1.0 ) + FT_Outline_Transform( outline, &scaleMat ); + + FT_Outline_Translate( outline, translate_value, 0 ); + + FT_Outline_EmboldenXY( outline, embolden_value, 0 ); + } + else + { +#endif + outline = &slot->outline; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + /* Need to get this PRIOR to embolden, otherwise bad things happen */ + FT_Outline_Get_CBox( outline, &cbox ); + + /* Various hacks that need to be turned into a new rule set */ + /*if ( !autohinted + && use_known_settings_on_selected_fonts + && mode == FT_RENDER_MODE_LCD + && slot->face->family_name + && slot->face->style_name + && ( strcasestr(slot->face->family_name, "Courier New" ) + && ( strcasestr(slot->face->style_name, "Regular" ) + || strcasestr(slot->face->style_name, "Italic" ) ) ) ) + FT_Outline_Embolden( outline, 24 );*/ + + if ( slot->face ) + { + if ( !autohinted && + use_known_settings_on_selected_fonts && + mode == FT_RENDER_MODE_LCD && + slot->face->family_name && + slot->face->style_name && + strcasestr( slot->face->family_name, "Times New Roman" ) && + strcasestr( slot->face->style_name, "Italic" ) ) + FT_Outline_EmboldenXY( outline, 12, 0 ); + + if ( use_known_settings_on_selected_fonts && + autohinted && + mode == FT_RENDER_MODE_LCD && + slot->face->family_name && + slot->face->style_name && + strcasestr(slot->face->family_name, "FreeSerif" ) && + strcasestr(slot->face->style_name, "Italic" ) ) + FT_Outline_EmboldenXY( outline, 8, 0 ); + + if ( global_embolden_x_value != 0 || global_embolden_y_value != 0 ) + FT_Outline_EmboldenXY( outline, + global_embolden_x_value, + global_embolden_y_value ); + + if ( ( bold_embolden_x_value != 0 || bold_embolden_y_value != 0 ) && + ( slot->face->style_name && + ( strcasestr(slot->face->style_name, "Bold" ) || + strcasestr(slot->face->style_name, "Black" ) || + ( slot->face->style_flags && + slot->face->style_flags & FT_STYLE_FLAG_BOLD ) ) ) ) + FT_Outline_EmboldenXY( outline, + bold_embolden_x_value, + bold_embolden_y_value ); + } + + FT_Outline_Copy( outline, outline_orig ); + } + + /* translate the outline to the new origin if needed */ + if ( align_called == 0 ) + { + FT_Pos enlarge_cbox = 0; + + /* enlarge for grayscale rendering */ + if ( mode == FT_RENDER_MODE_NORMAL ) + enlarge_cbox = 64; + + if ( origin ) + { + FT_Outline_Translate( outline, origin->x, origin->y ); + have_translated_origin = TRUE; + } + + /* compute the control box, and grid fit it */ + /*FT_Outline_Get_CBox( outline, &cbox );*/ + + cbox.xMin = FT_PIX_FLOOR( cbox.xMin - enlarge_cbox ); + cbox.yMin = FT_PIX_FLOOR( cbox.yMin ); + cbox.xMax = FT_PIX_CEIL( cbox.xMax + enlarge_cbox ); + cbox.yMax = FT_PIX_CEIL( cbox.yMax ); +#else if ( origin ) { - x_shift = origin->x; - y_shift = origin->y; + FT_Outline_Translate( outline, origin->x, origin->y ); + have_translated_origin = TRUE; } /* compute the control box, and grid fit it */ - /* taking into account the origin shift */ FT_Outline_Get_CBox( outline, &cbox ); - cbox.xMin = FT_PIX_FLOOR( cbox.xMin + x_shift ); - cbox.yMin = FT_PIX_FLOOR( cbox.yMin + y_shift ); - cbox.xMax = FT_PIX_CEIL( cbox.xMax + x_shift ); - cbox.yMax = FT_PIX_CEIL( cbox.yMax + y_shift ); - - x_shift -= cbox.xMin; - y_shift -= cbox.yMin; - - x_left = cbox.xMin >> 6; - y_top = cbox.yMax >> 6; + cbox.xMin = FT_PIX_FLOOR( cbox.xMin ); + cbox.yMin = FT_PIX_FLOOR( cbox.yMin ); + cbox.xMax = FT_PIX_CEIL( cbox.xMax ); + cbox.yMax = FT_PIX_CEIL( cbox.yMax ); +#endif width = (FT_ULong)( cbox.xMax - cbox.xMin ) >> 6; height = (FT_ULong)( cbox.yMax - cbox.yMin ) >> 6; @@ -166,16 +2621,29 @@ height_org = height; #endif + /* release old bitmap buffer */ + if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) + { + FT_FREE( bitmap->buffer ); + slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; + } + + /* allocate new one */ pitch = width; if ( hmul ) { - width *= 3; - pitch = FT_PAD_CEIL( width, 4 ); + width = width * 3; + pitch = FT_PAD_CEIL( width, 4 ); } if ( vmul ) height *= 3; + x_shift = cbox.xMin; + y_shift = cbox.yMin; + x_left = cbox.xMin >> 6; + y_top = cbox.yMax >> 6; + #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING if ( slot->library->lcd_filter_func ) @@ -185,33 +2653,25 @@ if ( hmul ) { - x_shift += 64 * ( extra >> 1 ); - x_left -= extra >> 1; + x_shift -= 64 * ( extra >> 1 ); width += 3 * extra; pitch = FT_PAD_CEIL( width, 4 ); + x_left -= extra >> 1; } if ( vmul ) { - y_shift += 64 * ( extra >> 1 ); - y_top += extra >> 1; + y_shift -= 64 * ( extra >> 1 ); height += 3 * extra; + y_top += extra >> 1; } } - #endif - - /* - * XXX: on 16bit system, we return an error for huge bitmap - * to prevent an overflow. - */ - if ( x_left > FT_INT_MAX || y_top > FT_INT_MAX || - x_left < FT_INT_MIN || y_top < FT_INT_MIN ) - { - error = FT_THROW( Invalid_Pixel_Size ); - goto Exit; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET } +#endif + /* Required check is (pitch * height < FT_ULONG_MAX), */ /* but we care realistic cases only. Always pitch <= width. */ if ( width > 0x7FFF || height > 0x7FFF ) @@ -222,6 +2682,22 @@ goto Exit; } + bitmap->pixel_mode = FT_PIXEL_MODE_GRAY; + bitmap->num_grays = 256; + bitmap->width = (unsigned int)width; + bitmap->rows = (unsigned int)height; + bitmap->pitch = pitch; + + /* translate outline to render it into the bitmap */ +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( align_called == 0 ) + { +#endif + FT_Outline_Translate( outline, -x_shift, -y_shift ); + have_outline_shifted = TRUE; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + } +#endif /* release old bitmap buffer */ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) { @@ -229,7 +2705,6 @@ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP; } - /* allocate new one */ if ( FT_ALLOC( bitmap->buffer, (FT_ULong)( pitch * height ) ) ) goto Exit; else @@ -237,23 +2712,6 @@ slot->internal->flags |= FT_GLYPH_OWN_BITMAP; - slot->format = FT_GLYPH_FORMAT_BITMAP; - slot->bitmap_left = (FT_Int)x_left; - slot->bitmap_top = (FT_Int)y_top; - - bitmap->pixel_mode = FT_PIXEL_MODE_GRAY; - bitmap->num_grays = 256; - bitmap->width = (unsigned int)width; - bitmap->rows = (unsigned int)height; - bitmap->pitch = pitch; - - /* translate outline to render it into the bitmap */ - if ( x_shift || y_shift ) - { - FT_Outline_Translate( outline, x_shift, y_shift ); - have_outline_shifted = TRUE; - } - /* set up parameters */ params.target = bitmap; params.source = outline; @@ -299,9 +2757,153 @@ if ( error ) goto Exit; +#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + if ( ppem <= MAX_PPEM && ppem >= MIN_PPEM ) + { + if ( align_called == 0 && cur_width / ppem < 10 && + ( alignment_strength > 0 || fitting_strength > 0 ) ) + _lcd_stem_align ( bitmap, + mode, + slot, + &translate_value, + &scale_value, + alignment_strength, + fitting_strength, + &embolden_value ); + + if ( align_called == 0 && + ( translate_value != 0 || scale_value != 1.0 ) ) + { + align_called = 1; + goto RERENDER; + } + + if ( mode == FT_RENDER_MODE_LCD ) + { + + if ( fringe_filter_strength > 0 /*&& autohinted*/ ) + _ft_lcd_fringe_filter( bitmap, + mode, + fringe_filter_strength, + slot->library ); + + /*if ( autohinted) + _ft_lcd_stem_end_filter( bitmap, mode, 100, slot->library );*/ + + if ( gamma_correction_lt > 0 && gamma_correction_value != 1.0 ) + _ft_lcd_gamma_correction_correction( bitmap, + mode, + slot, + gamma_correction_lt, + gamma_correction_value ); + + chromeos_cutoff = (FT_Byte)( 0.5 * 255.0 ) + * ( chromeos_style_sharpening_strength / 100.0 ); + chromeos_gamma_value = 1; + + if ( chromeos_style_sharpening_strength > 0 ) + _ft_lcd_chromeos_sharpen( bitmap, + mode, + chromeos_cutoff, + chromeos_gamma_value ); + + if ( ppem > 8 ) + if ( windows_style_sharpening_strength > 0 ) + _ft_lcd_windows_sharpen( bitmap, + mode, + windows_style_sharpening_strength, + slot->library ); + + if ( autohinted && + ( cur_width * 100 ) / 64 + > autohint_horizontal_stem_darken_strength && + autohint_horizontal_stem_darken_strength != 0 ) + autohint_horizontal_stem_darken_strength = ( cur_width * 100 ) / 64; + + if ( autohint_horizontal_stem_darken_strength > 100) + autohint_horizontal_stem_darken_strength = 100; + + /* only do on autohinted fonts */ + /* Necessary to do on some non-thin fonts, which is why it is outside */ + /* of the below conditional */ + if ( autohint_horizontal_stem_darken_strength > 0 && autohinted ) + _ft_lcd_darken_x ( bitmap, + mode, + autohint_horizontal_stem_darken_strength, + slot->library ); + + /* Enhance thin fonts */ + if ( autohinted ) + { + /* if forcibly set use that, otherwise make a good estimate */ + float contrast, brightness; + ftinf_get_bc( slot->face->family_name, ppem, &brightness, &contrast); + if ( slot->face && !_ft_bitmap_bc ( bitmap, brightness, contrast ) ) + { + FT_Bool is_fixed_name = FALSE; + + if ( slot->face->family_name && + strcasestr(slot->face->family_name, "Mono" ) ) + is_fixed_name = TRUE; + + /* Darken vertical stems */ + _ft_lcd_darken_y ( bitmap, + mode, + autohint_vertical_stem_darken_strength, + slot->library ); + + /* Adjust brightness / contrast automatically based on stem width */ + if ( cur_width != 0 && cur_width < 30 ) + cur_width = 30; + + if ( cur_width >= 30 && cur_width <= 60 ) + { + float ppem_factor = sliding_scale ( 5, 11, 0.0, 1.0, ppem ); + float brightness_factor = sliding_scale ( 30, 52, -.3, 0.0, + cur_width ); + float contrast_factor = sliding_scale ( 30, 52, .45, 0.0, + cur_width ); + _ft_bitmap_bc ( bitmap, + ppem_factor * brightness_factor, + ppem_factor * contrast_factor ); + + /* Only cap variable width thin-stemmed fonts */ + if ( !FT_IS_FIXED_WIDTH( slot->face ) && !is_fixed_name ) + _ft_bitmap_cap ( bitmap, + ( cur_width * 150 ) / 64, + slot->library ); + } + } + } + + + if ( slot->library->lcd_filter_func ) + slot->library->lcd_filter_func( bitmap, mode, slot->library ); + + if ( grayscale_filter_strength > 0 ) + _ft_lcd_grayscale_filter( bitmap, + mode, + grayscale_filter_strength, + slot->library ); + + } + + /* Global values */ + if ( brightness_value != 0 || contrast_value != 0 ) + _ft_bitmap_bc ( bitmap, + (float)brightness_value / 300.0, + (float)contrast_value / 300.0); + + FT_Outline_Done( slot->library, outline_orig ); + } + else if ( mode == FT_RENDER_MODE_LCD && + slot->library->lcd_filter_func ) + slot->library->lcd_filter_func( bitmap, mode, slot->library ); +#else if ( slot->library->lcd_filter_func ) slot->library->lcd_filter_func( bitmap, mode, slot->library ); +#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ #else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ /* render outline into bitmap */ @@ -359,6 +2961,20 @@ #endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ + /* + * XXX: on 16bit system, we return an error for huge bitmap + * to prevent an overflow. + */ + if ( x_left > FT_INT_MAX || y_top > FT_INT_MAX ) + { + error = FT_THROW( Invalid_Pixel_Size ); + goto Exit; + } + + slot->format = FT_GLYPH_FORMAT_BITMAP; + slot->bitmap_left = (FT_Int)x_left; + slot->bitmap_top = (FT_Int)y_top; + /* everything is fine; don't deallocate buffer */ have_buffer = FALSE; @@ -366,7 +2982,9 @@ Exit: if ( have_outline_shifted ) - FT_Outline_Translate( outline, -x_shift, -y_shift ); + FT_Outline_Translate( outline, x_shift, y_shift ); + if ( have_translated_origin ) + FT_Outline_Translate( outline, -origin->x, -origin->y ); if ( have_buffer ) { FT_FREE( bitmap->buffer ); diff --git a/src/truetype/ttinterp.c b/src/truetype/ttinterp.c index 1c1d7de..bd37b44 100644 --- a/src/truetype/ttinterp.c +++ b/src/truetype/ttinterp.c @@ -5792,6 +5792,7 @@ if ( exc->ignore_x_mode && exc->GS.freeVector.x != 0 && + exc->GS.freeVector.y == 0 && !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) control_value_cutin = 0; }