diff options
Diffstat (limited to '03-infinality-2.6-2015.08.24.patch')
-rw-r--r-- | 03-infinality-2.6-2015.08.24.patch | 4839 |
1 files changed, 4839 insertions, 0 deletions
diff --git a/03-infinality-2.6-2015.08.24.patch b/03-infinality-2.6-2015.08.24.patch new file mode 100644 index 000000000000..0b01a13db306 --- /dev/null +++ b/03-infinality-2.6-2015.08.24.patch @@ -0,0 +1,4839 @@ +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="<ftmodule.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 eda772e..d408a5d 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 4970945..3df05be 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 3065895..214c217 100644 +--- a/src/autofit/aflatin.c ++++ b/src/autofit/aflatin.c +@@ -24,6 +24,7 @@ + #include "afpic.h" + #include "aflatin.h" + #include "aferrors.h" ++#include "strings.h" + + + #ifdef AF_CONFIG_OPTION_USE_WARPER +@@ -40,6 +41,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 ) +@@ -899,8 +904,33 @@ + FT_Pos delta; + AF_LatinAxis axis; + FT_UInt nn; ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++ int checked_adjust_heights_env = 0; ++ FT_Bool adjust_heights = FALSE; + + ++ if ( checked_adjust_heights_env == 0 ) ++ { ++ char *adjust_heights_env = ++ getenv( "INFINALITY_FT_AUTOHINT_INCREASE_GLYPH_HEIGHTS" ); ++ if ( adjust_heights_env != NULL ) ++ { ++ if ( strcasecmp(adjust_heights_env, "default" ) != 0 ) ++ { ++ if ( strcasecmp(adjust_heights_env, "true") == 0 ) ++ adjust_heights = TRUE; ++ else if ( strcasecmp(adjust_heights_env, "1") == 0 ) ++ adjust_heights = TRUE; ++ else if ( strcasecmp(adjust_heights_env, "on") == 0 ) ++ adjust_heights = TRUE; ++ else if ( strcasecmp(adjust_heights_env, "yes") == 0 ) ++ adjust_heights = TRUE; ++ } ++ } ++ checked_adjust_heights_env = 1; ++ } ++#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ ++ + if ( dim == AF_DIMENSION_HORZ ) + { + scale = scaler->x_scale; +@@ -927,7 +957,7 @@ + { + AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT]; + AF_LatinBlue blue = NULL; +- ++ int threshold = 40; + + for ( nn = 0; nn < Axis->blue_count; nn++ ) + { +@@ -937,7 +967,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; +@@ -1056,7 +1091,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; +@@ -1107,7 +1148,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 + +@@ -1945,7 +1991,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 ) + { +@@ -2120,8 +2169,31 @@ + 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; ++ FT_UInt checked_autohint_snap_stem_height = 0; + + ++ if ( checked_autohint_snap_stem_height == 0 ) ++ { ++ char *autohint_snap_stem_height_env = ++ getenv( "INFINALITY_FT_AUTOHINT_SNAP_STEM_HEIGHT" ); ++ if ( autohint_snap_stem_height_env != NULL ) ++ { ++ sscanf ( autohint_snap_stem_height_env, "%u", ++ &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; ++ } ++ checked_autohint_snap_stem_height = 1; ++ } ++ ++ 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; +@@ -2131,9 +2203,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 ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) || ++ 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 ) ) || + ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ) ++#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ + { + /* smooth hinting process: very lightly quantize the stem width */ + +@@ -2193,6 +2329,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 */ + +@@ -2200,7 +2339,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 */ +@@ -2263,6 +2405,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; + +@@ -2284,6 +2452,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; + +@@ -2844,8 +3014,32 @@ + int dim; + + AF_LatinAxis axis; ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++ FT_Int emboldening_strength = 0; ++ FT_Bool checked_use_various_tweaks_env = FALSE; ++ FT_Bool use_various_tweaks = FALSE; + +- ++ if ( !checked_use_various_tweaks_env ) ++ { ++ char *use_various_tweaks_env = ++ getenv( "INFINALITY_FT_USE_VARIOUS_TWEAKS" ); ++ if ( use_various_tweaks_env != NULL ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "default" ) != 0 ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "true") == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "1") == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "on") == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "yes") == 0 ) ++ use_various_tweaks = TRUE; ++ } ++ } ++ checked_use_various_tweaks_env = TRUE; ++ } ++#endif /* FT_CONFIG_OPTION_INFINALITY_PATCHSET */ + error = af_glyph_hints_reload( hints, outline ); + if ( error ) + goto Exit; +@@ -2913,7 +3107,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 6855492..736f251 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/ftlcdfil.c b/src/base/ftlcdfil.c +index ff6f7e9..7b91443 100644 +--- a/src/base/ftlcdfil.c ++++ b/src/base/ftlcdfil.c +@@ -23,6 +23,9 @@ + #include FT_IMAGE_H + #include FT_INTERNAL_OBJECTS_H + ++#include <math.h> ++#include <string.h> ++#include <strings.h> + + #ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING + +@@ -309,10 +312,53 @@ + { 0x00, 0x55, 0x56, 0x55, 0x00 }; + /* the values here sum up to a value larger than 256, */ + /* providing a cheap gamma correction */ +- static const FT_Byte default_filter[5] = ++ static FT_Byte default_filter[5] = + { 0x10, 0x40, 0x70, 0x40, 0x10 }; ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++ int checked_filter_params_env = 0; + ++ if ( checked_filter_params_env == 0 ) ++ { ++ char *filter_params = getenv( "INFINALITY_FT_FILTER_PARAMS" ); ++ if ( filter_params != NULL && strcmp( filter_params, "" ) != 0 ) ++ { ++ float f1, f2, f3, f4, f5; + ++ if ( strcasecmp( filter_params, "default" ) != 0 ) ++ { ++ int args_assigned = 0; ++ args_assigned = sscanf ( filter_params, ++ "%f %f %f %f %f", ++ &f1, &f2, &f3, &f4, &f5 ); ++ ++ if ( args_assigned == 5 ) ++ { ++ if ( f1 + f2 + f3 + f4 + f5 > 5 ) ++ { ++ /* Assume we were given integers instead of floats */ ++ /* 0 to 100 */ ++ default_filter[0] = (FT_Byte) ( f1 * 2.55f + 0.5f ); ++ default_filter[1] = (FT_Byte) ( f2 * 2.55f + 0.5f ); ++ default_filter[2] = (FT_Byte) ( f3 * 2.55f + 0.5f ); ++ default_filter[3] = (FT_Byte) ( f4 * 2.55f + 0.5f ); ++ default_filter[4] = (FT_Byte) ( f5 * 2.55f + 0.5f ); ++ } ++ else ++ { ++ /* Assume we were given floating point values */ ++ /* 0 to 1.0 */ ++ default_filter[0] = (FT_Byte) ( f1 * 255.0f + 0.5f ); ++ default_filter[1] = (FT_Byte) ( f2 * 255.0f + 0.5f ); ++ default_filter[2] = (FT_Byte) ( f3 * 255.0f + 0.5f ); ++ default_filter[3] = (FT_Byte) ( f4 * 255.0f + 0.5f ); ++ default_filter[4] = (FT_Byte) ( f5 * 255.0f + 0.5f ); ++ } ++ } ++ } ++ } ++ checked_filter_params_env = 1; ++ } ++#endif + if ( !library ) + return FT_THROW( Invalid_Library_Handle ); + +diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c +index 926ac0a..8a166a5 100644 +--- a/src/base/ftobjs.c ++++ b/src/base/ftobjs.c +@@ -67,6 +67,11 @@ + + #define GRID_FIT_METRICS + ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++#include <strings.h> ++#include <stdlib.h> ++#include "../autofit/aflatin.h" ++#endif + + FT_BASE_DEF( FT_Pointer ) + ft_service_list_lookup( FT_ServiceDesc service_descriptors, +@@ -543,6 +548,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, +@@ -601,8 +625,40 @@ + FT_Bool autohint = FALSE; + FT_Module hinter; + TT_Face ttface = (TT_Face)face; ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + ++ int checked_use_various_tweaks_env = FALSE; ++ FT_Bool use_various_tweaks = FALSE; ++ ++ if ( !checked_use_various_tweaks_env ) ++ { ++ char *use_various_tweaks_env = ++ getenv( "INFINALITY_FT_USE_VARIOUS_TWEAKS" ); ++ ++ if ( use_various_tweaks_env != NULL ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "default" ) != 0 ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "true") == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "1") == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "on") == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "yes") == 0 ) ++ use_various_tweaks = TRUE; ++ } ++ } ++ checked_use_various_tweaks_env = 1; ++ } + ++ /* 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 ); + +@@ -690,6 +746,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 */ + /* */ +@@ -735,6 +803,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 ef66b3c..c3e4c71 100644 +--- a/src/base/ftoutln.c ++++ b/src/base/ftoutln.c +@@ -910,7 +910,34 @@ + FT_Vector* points; + FT_Int c, first, last; + FT_Int orientation; ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++ int checked_use_various_tweaks_env = 0; ++ FT_Bool use_various_tweaks = FALSE; + ++ if ( checked_use_various_tweaks_env == 0 ) ++ { ++ char *use_various_tweaks_env ++ = getenv( "INFINALITY_FT_USE_VARIOUS_TWEAKS" ); ++ if ( use_various_tweaks_env != NULL ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "default" ) != 0 ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "true" ) == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "1" ) == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "on" ) == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "yes" ) == 0 ) ++ use_various_tweaks = TRUE; ++ } ++ } ++ checked_use_various_tweaks_env = 1; ++ } ++ ++ 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..62352e1 100644 +--- a/src/base/ftsynth.c ++++ b/src/base/ftsynth.c +@@ -93,7 +93,32 @@ + FT_Face face; + FT_Error error; + FT_Pos xstr, ystr; ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++ int checked_use_various_tweaks_env = 0; ++ FT_Bool use_various_tweaks = FALSE; ++ ++ if ( checked_use_various_tweaks_env == 0 ) ++ { ++ char *use_various_tweaks_env ++ = getenv( "INFINALITY_FT_USE_VARIOUS_TWEAKS" ); ++ if ( use_various_tweaks_env != NULL ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "default" ) != 0 ) ++ { ++ if ( strcasecmp(use_various_tweaks_env, "true" ) == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "1" ) == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "on" ) == 0 ) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp(use_various_tweaks_env, "yes" ) == 0 ) ++ use_various_tweaks = TRUE; ++ } + ++ } ++ checked_use_various_tweaks_env = 1; ++ } ++#endif + + if ( !slot ) + return; +@@ -111,8 +136,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 +183,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/smooth/ftsmooth.c b/src/smooth/ftsmooth.c +index 3620550..a92e003 100644 +--- a/src/smooth/ftsmooth.c ++++ b/src/smooth/ftsmooth.c +@@ -26,102 +26,3575 @@ + + #include "ftsmerrs.h" + ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++#include <math.h> ++#include FT_BITMAP_H ++#include <strings.h> ++#include "../autofit/aflatin.h" ++#include FT_OUTLINE_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 ++ ft_smooth_init( FT_Renderer render ) ++ { ++ FT_Library library = FT_MODULE_LIBRARY( render ); ++ ++ ++ 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 ); ++ } ++ ++ ++ int ++ pseudo_gamma ( int val, float value ) ++ { ++ return 256 * ( 1.0 - pow( ( 1.0 - (float)val / 256.0 ), 1.0 / value ) ); ++ } ++ ++ ++ ++ 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 ) ; ++ } ++ } ++ } ++ ++ ++ /* 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; ++ } ++ } ++ } ++ ++ ++ ++ /*************************************************************************/ ++ /* */ ++ /* */ ++ /* */ ++ /* */ ++ /* */ ++ /* */ ++ ++ ++ typedef struct SA_Rule_ ++ { ++ const char family[32]; ++ const int ppem[5]; ++ ++ } SA_Rule; ++ ++#define STEM_WIDTH_2_PPEM 18 ++#define MAX_PPEM 100 ++ ++ ++ ++/* "Font name", {ppem where stem width becomes 1, ++ * ppem where stem width becomes 2... etc.} */ ++/* 100 means auto-calculate */ ++#define SNAPPING_STEM_WIDTHS_RULES_SIZE 21 ++ SA_Rule SNAPPING_STEM_WIDTHS_Rules ++ [SNAPPING_STEM_WIDTHS_RULES_SIZE] = ++ { ++ { "Andale Mono", {10, 21, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Arial Narrow", {10, 21, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Calibri", {10, 19, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Cantarell", {10, 22, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Century Gothic", {10, 22, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Comfortaa", {10, 19, 22, MAX_PPEM, MAX_PPEM} }, ++ { "Consolas", {10, 20, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Corbel", {10, 21, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Futura", {10, 14, STEM_WIDTH_2_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Gill Sans", {10, 17, STEM_WIDTH_2_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Helvetica CY", {10, 23, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Inconsolata", {10, 23, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Liberation Sans Narrow", {10, 22, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Liberation Sans", {10, 19, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Lucida Grande", {10, 16, STEM_WIDTH_2_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Lucida Sans Unicode", {10, 16, STEM_WIDTH_2_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Luxi Sans", {10, 17, STEM_WIDTH_2_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Open Sans", {10, 20, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Rokkitt", {10, 21, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Segoe UI", {10, 23, MAX_PPEM, MAX_PPEM, MAX_PPEM} }, ++ { "Trebuchet MS", {10, 17, STEM_WIDTH_2_PPEM, MAX_PPEM, MAX_PPEM} }, ++ }; ++ ++ ++/* "Font name", {ppem, scale_up=1|scale_down=0} */ ++#define SNAPPING_STEM_SCALING_RULES_SIZE 31 ++ SA_Rule SNAPPING_STEM_SCALING_Rules ++ [SNAPPING_STEM_SCALING_RULES_SIZE] = ++ { ++ { "Andale Mono", {11, 1,} }, ++ { "Bitstream Vera Sans", {12, 1,} }, ++ { "Calibri", {15, 1,} }, ++ { "Calibri", {17, 1,} }, ++ { "Calibri", {18, 1,} }, ++ { "Candara", {14, 1,} }, ++ { "Candara", {17, 1,} }, ++ { "Canwell", {13, 0,} }, ++ { "Comfortaa", {11, 0,} }, ++ { "Consolas", {11, 1,} }, ++ { "DejaVu Sans", {12, 1,} }, ++ { "Freesans", {16, 0,} }, ++ { "Freeserif", {13, 1,} }, ++ { "Freeserif", {17, 1,} }, ++ { "Inconsolata", {12, 1,} }, ++ { "Inconsolata", {15, 1,} }, ++ { "Lucida Grande", {13, 1,} }, ++ { "Myriad Pro", {14, 1,} }, ++ { "Myriad Pro", {17, 1,} }, ++ { "Nina", {11, 0,} }, ++ { "Nina", {12, 0,} }, ++ { "Nina", {13, 0,} }, ++ { "Optima", {17, 1,} }, ++ { "Raleway", {15, 0,} }, ++ { "Samba", {11, 0,} }, ++ { "Times New Roman", {17, 1,} }, ++ { "Trebuchet MS", {17, 0,} }, ++ { "Trebuchet MS", {13, 0,} }, ++ { "Trebuchet MS", {20, 1,} }, ++ { "Verdana", {12, 1,} }, ++ { "Verdana", {15, 1,} }, ++ }; ++ ++ ++/* "Font name", {ppem, scale_up=1|scale_down=0} */ ++#define SNAPPING_M_RULES_SIZE 9 ++ SA_Rule SNAPPING_M_Rules ++ [SNAPPING_M_RULES_SIZE] = ++ { ++ { "Courier New", {13, 1,} }, ++ { "Courier New", {14, 1,} }, ++ { "Courier", {13, 1,} }, ++ { "Courier", {14, 1,} }, ++ { "Droid Sans Mono", {12, 0,} }, ++ { "Bitstream Vera Sans", {12, 0,} }, ++ { "DejaVu Sans", {12, 0,} }, ++ { "Essential PragmataPro", {13, 0,} }, ++ { "Essential PragmataPro", {14, 0,} }, ++ }; ++ ++ ++/* "Font name", {ppem, ppem} */ ++#define SNAPPING_SYNTHESIZE_STEMS_RULES_SIZE 1 ++ SA_Rule SNAPPING_SYNTHESIZE_STEMS_Rules ++ [SNAPPING_SYNTHESIZE_STEMS_RULES_SIZE] = ++ { ++ { "---", {13, 13,} }, ++ }; ++ ++ ++/* "Font name", {ppem, ppem} */ ++#define SNAPPING_NO_BEARING_CORRECTION_RULES_SIZE 1 ++ SA_Rule SNAPPING_NO_BEARING_CORRECTION_Rules ++ [SNAPPING_NO_BEARING_CORRECTION_RULES_SIZE] = ++ { ++ { "Times New Roman", {0, 100,} }, ++ }; ++ ++ ++/* "Font name", {ppem, ppem} */ ++#define SNAPPING_EDGE_DETECTION_RULES_SIZE 8 ++ SA_Rule SNAPPING_EDGE_DETECTION_Rules ++ [SNAPPING_EDGE_DETECTION_RULES_SIZE] = ++ { ++ { "Tahoma", {11, 11,} }, ++ { "Courier New", {10, 12,} }, ++ { "Arial", {11, 11,} }, ++ { "Arial", {13, 13,} }, ++ { "Liberation Sans", {11, 11,} }, ++ { "FreeSans", {11, 11,} }, ++ { "FreeSans", {13, 13,} }, ++ { "Palatino Linotype", {0, 100,} }, ++ }; ++ ++/* "Font name", {ppem, translate_value} */ ++#define SNAPPING_STEM_TRANSLATING_RULES_SIZE 6 ++ SA_Rule SNAPPING_STEM_TRANSLATING_Rules ++ [SNAPPING_STEM_TRANSLATING_RULES_SIZE] = ++ { ++ { "Arial", {11, 32,} }, ++ { "Arial Unicode MS", {11, 32,} }, ++ { "FreeSans", {11, 32,} }, ++ { "Arimo", {11, 32,} }, ++ { "Liberation Sans", {11, 32,} }, ++ { "Tahoma", {11, 32,} }, ++ }; ++ ++/* "Font name", {ppem, translate_value} */ ++#define SNAPPING_STEM_TRANSLATING_ONLY_RULES_SIZE 74 ++ SA_Rule SNAPPING_STEM_TRANSLATING_ONLY_Rules ++ [SNAPPING_STEM_TRANSLATING_ONLY_RULES_SIZE] = ++ { ++ { "Arial Unicode MS", {10, 16,} }, ++ { "Arial Unicode MS", {8, 32,} }, ++ { "Arial Unicode MS", {9, 32,} }, ++ { "Arial", {10, 16,} }, ++ { "Arial", {8, 32,} }, ++ { "Arial", {9, 32,} }, ++ { "Arial", {16, -24,} }, ++ { "Arimo", {10, 8,} }, ++ { "Arimo", {8, 32,} }, ++ { "Arimo", {9, 32,} }, ++ { "Bitstream Vera Sans", {8, 16,} }, ++ { "Calibri", {10, 16,} }, ++ { "Calibri", {15, 0,} }, ++ { "Candara", {10, 16,} }, ++ { "Cantarell", {11, 0} }, ++ { "Cantarell", {12, 0} }, ++ { "Consolas", {8, 32,} }, ++ { "Consolas", {9, 32,} }, ++ { "Corbel", {10, 16,} }, ++ { "Courier", {13, 16,} }, ++ { "Courier", {15, 0,} }, ++ { "Dejavu Sans Mono", {7, 16,} }, ++ { "Dejavu Sans Mono", {8, 32,} }, ++ { "Dejavu Sans Mono", {9, 16,} }, ++ { "Dejavu Sans", {8, 16,} }, ++ { "Dejavu Sans", {15, -20,} }, ++ { "Droid Sans", {8, 16,} }, ++ { "Droid Sans", {9, 16,} }, ++ { "Freesans", {10, 16,} }, ++ { "Freesans", {9, 8,} }, ++ { "Georgia", {13, 16,} }, ++ { "Georgia", {14, 16,} }, ++ { "Georgia", {15, 0,} }, ++ { "Inconsolata", {10, 24,} }, ++ { "Inconsolata", {9, 32,} }, ++ { "Liberation Sans", {10, 8,} }, ++ { "Liberation Sans", {8, 32,} }, ++ { "Liberation Sans", {9, 32,} }, ++ { "Lucida Grande", {13, 24,} }, ++ { "Lucida Grande", {14, 24,} }, ++ { "Lucida Grande", {8, 16,} }, ++ { "Lucida Grande", {9, 16,} }, ++ { "Lucida Sans Unicode", {13, 24,} }, ++ { "Lucida Sans Unicode", {14, 24,} }, ++ { "Lucida Sans Unicode", {8, 16,} }, ++ { "Lucida Sans Unicode", {9, 16,} }, ++ { "Microsoft Sans Serif", {10, 16,} }, ++ { "Microsoft Sans Serif", {8, 32,} }, ++ { "Microsoft Sans Serif", {9, 32,} }, ++ { "Myriad Pro", {10, 16,} }, ++ { "Myriad Pro", {11, 0,} }, ++ { "Myriad Pro", {9, 16,} }, ++ { "Open Sans", {10, 16,} }, ++ { "Open Sans", {9, 16,} }, ++ { "Optima", {10, 0} }, ++ { "Optima", {11, 0} }, ++ { "Optima", {12, 0} }, ++ { "Segoe UI", {10, 0,} }, ++ { "Segoe UI", {7, 32,} }, ++ { "Segoe UI", {8, 16,} }, ++ { "Segoe UI", {9, 24,} }, ++ { "Tahoma", {7, 32,} }, ++ { "Tahoma", {8, 32,} }, ++ { "Tahoma", {9, 32,} }, ++ { "Times New Roman", {17, 8,} }, ++ { "Trebuchet MS", {10, 16,} }, ++ { "Trebuchet MS", {11, 0,} }, ++ { "Trebuchet MS", {8, 32,} }, ++ { "Trebuchet MS", {9, 32,} }, ++ { "Verdana", {8, 16,} }, ++ { "Verdana", {15, 16,} }, ++ { "Verdana", {14, 32,} }, ++ { "Verdana", {18, 32,} }, ++ { "Verdana", {19, 24,} }, ++ }; ++ ++ ++/* "Font name", {start ppem, end ppem} */ ++#define ALWAYS_USE_100_RULES_SIZE 46 ++ SA_Rule ALWAYS_USE_100_Rules ++ [ALWAYS_USE_100_RULES_SIZE] = ++ { ++ { "Andale Mono", {0, MAX_PPEM,} }, ++ { "Arial Unicode MS", {0, MAX_PPEM,} }, ++ { "Arial", {0, MAX_PPEM,} }, ++ { "Arimo", {0, MAX_PPEM,} }, ++ { "Bitstream Vera Sans Mono", {0, MAX_PPEM,} }, ++ { "Bitstream Vera Sans", {10, 14,} }, ++ { "Bitstream Vera Sans", {16, 17,} }, ++ { "Calibri", {23, MAX_PPEM,} }, ++ { "Consolas", {0, MAX_PPEM,} }, ++ { "Courier New", {12, 12,} }, ++ { "Courier", {0, MAX_PPEM,} }, ++ { "Cousine", {0, MAX_PPEM,} }, ++ { "DejaVu Sans Mono", {0, MAX_PPEM,} }, ++ { "DejaVu Sans", {10, 14,} }, ++ { "DejaVu Sans", {16, 17,} }, ++ { "Droid Sans", {12, 12,} }, ++ { "Droid Sans", {15, 15,} }, ++ { "FreeMono", {0, MAX_PPEM,} }, ++ { "FreeSans", {0, MAX_PPEM,} }, ++ { "Liberation Mono", {0, MAX_PPEM,} }, ++ { "Lucida Console", {0, MAX_PPEM,} }, ++ { "Luxi Sans", {13, 13,} }, ++ { "Microsoft Sans Serif", {0, MAX_PPEM,} }, ++ { "Monaco", {0, MAX_PPEM,} }, ++ { "Segoe UI", {11, 12,} }, ++ { "Segoe UI", {14, 14,} }, ++ { "Tahoma", {11, 11,} }, ++ { "Tahoma", {14, MAX_PPEM,} }, ++ { "Times New Roman", {14, 14,} }, ++ { "Times New Roman", {16, 16,} }, ++ { "Trebuchet MS", {13, 13,} }, ++ { "Ubuntu", {12, 13,} }, ++ { "Ubuntu", {15, 15,} }, ++ { "Verdana", {0, 14,} }, ++ { "Verdana", {16, MAX_PPEM,} }, ++ { "Pragmata", {0, MAX_PPEM,} }, ++ { "Essential PragmataPro", {0, MAX_PPEM,} }, ++ }; ++ ++ ++ ++ ++#define AUTOHINT_BRIGHTNESS_RULES_SIZE 3 ++ SA_Rule BRIGHTNESS_Rules ++ [AUTOHINT_BRIGHTNESS_RULES_SIZE] = ++ { ++ { "Baskerville", {0, -20,} }, ++ { "Garamond", {0, -20,} }, ++ { "Optima", {0, -20,} }, ++ }; ++ ++#define AUTOHINT_CONTRAST_RULES_SIZE 3 ++ SA_Rule CONTRAST_Rules ++ [AUTOHINT_CONTRAST_RULES_SIZE] = ++ { ++ { "Baskerville", {0, 25,} }, ++ { "Garamond", {0, 25,} }, ++ { "Optima", {0, 25,} }, ++ }; ++ ++#if 0 ++#define STEM_SPACING_RULES_SIZE 3 ++ SA_Rule STEM_SPACING_Rules ++ [STEM_SPACING_RULES_SIZE] = ++ { ++ { "Tahoma", {10, 12, 18, 18, 30} }, ++ { "Arial", {10, 11, 23, 25, 30} }, ++ { "Freesans", {10, 12, 18, 18, 30} }, ++ }; ++ ++#define STEM_START_RULES_SIZE 3 ++ SA_Rule STEM_START_Rules ++ [STEM_START_RULES_SIZE] = ++ { ++ { "Tahoma", {14, 17, 30, 100, 100} }, ++ { "Arial", {11, 18, 23, 30, 30} }, ++ { "Freesans", {10, 18, 18, 25, 30} }, ++ }; ++#endif ++ ++ typedef struct Stem_Data_ ++ { ++ 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; ++ FT_Int brightness; ++ FT_Int contrast; ++ FT_Bool use_100; ++ FT_Bool synth_stems; ++ FT_Bool edge_detection; ++ FT_Bool bearing_correction; ++ FT_Int m; ++ } Stem_Data; ++ ++ ++ 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; ++ } ++ ++ ++ FT_LOCAL_DEF( void ) ++ sa_fill_known_stem_values ( ++ FT_String* family, ++ int ppem, ++ FT_String* style, ++ FT_UInt num_stems, ++ Stem_Data* known_stem_values ) ++ { ++ FT_Int i, j; ++ if (verbose) printf("%s ", family); ++ ++ i = 0; ++ while ( i < SNAPPING_STEM_WIDTHS_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_STEM_WIDTHS_Rules[i].family, ++ family ) == 0 ) ) ++ { ++ j = 0; ++ known_stem_values->stem_width = 1; ++ ++ while (j < 4) ++ { ++ if ( SNAPPING_STEM_WIDTHS_Rules[i].ppem[j] == MAX_PPEM ) ++ { ++ known_stem_values->stem_width = -1; /* use default */ ++ j = 5; ++ i = SNAPPING_STEM_WIDTHS_RULES_SIZE; ++ } ++ else if ( ppem < SNAPPING_STEM_WIDTHS_Rules[i].ppem[j] ) ++ { ++ known_stem_values->stem_width = j; ++ j = 5; ++ i = SNAPPING_STEM_WIDTHS_RULES_SIZE; ++ } ++ j++; ++ } ++ } ++ i++; ++ } ++ ++ i = 0; ++ while ( i < SNAPPING_STEM_SCALING_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_STEM_SCALING_Rules[i].family, ++ family ) == 0 ) ) ++ { ++ known_stem_values->stem_scaling = -1; /* default */ ++ ++ if ( ppem == SNAPPING_STEM_SCALING_Rules[i].ppem[0] ) ++ { ++ known_stem_values->stem_scaling ++ = SNAPPING_STEM_SCALING_Rules[i].ppem[1]; ++ i = SNAPPING_STEM_SCALING_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ ++ i = 0; ++ while ( i < SNAPPING_M_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_M_Rules[i].family, family ) == 0 ) ) ++ { ++ known_stem_values->m = -1; /* default */ ++ ++ if ( ppem == SNAPPING_M_Rules[i].ppem[0] ) ++ { ++ known_stem_values->m = SNAPPING_M_Rules[i].ppem[1]; ++ i = SNAPPING_M_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ i = 0; ++ while ( i < SNAPPING_STEM_TRANSLATING_ONLY_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_STEM_TRANSLATING_ONLY_Rules[i].family, ++ family ) == 0 ) ) ++ { ++ known_stem_values->stem_translating_only = -1024; /* default */ ++ ++ if ( ppem == SNAPPING_STEM_TRANSLATING_ONLY_Rules[i].ppem[0] || ++ SNAPPING_STEM_TRANSLATING_ONLY_Rules[i].ppem[0] == 0 ) ++ { ++ known_stem_values->stem_translating_only ++ = SNAPPING_STEM_TRANSLATING_ONLY_Rules[i].ppem[1]; ++ i = SNAPPING_STEM_TRANSLATING_ONLY_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ i = 0; ++ while ( i < SNAPPING_STEM_TRANSLATING_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_STEM_TRANSLATING_Rules[i].family, ++ family ) == 0 ) ) ++ { ++ known_stem_values->stem_translating = 0; /* default */ ++ ++ if ( ppem == SNAPPING_STEM_TRANSLATING_Rules[i].ppem[0] || ++ SNAPPING_STEM_TRANSLATING_Rules[i].ppem[0] == 0 ) ++ { ++ known_stem_values->stem_translating ++ = SNAPPING_STEM_TRANSLATING_Rules[i].ppem[1]; ++ i = SNAPPING_STEM_TRANSLATING_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ ++ i = 0; ++ while ( i < ALWAYS_USE_100_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( ALWAYS_USE_100_Rules[i].family, family ) == 0 ) ) ++ { ++ known_stem_values->use_100 = FALSE; /* default */ ++ ++ if ( ppem >= ALWAYS_USE_100_Rules[i].ppem[0] && ++ ppem <= ALWAYS_USE_100_Rules[i].ppem[1] ) ++ { ++ known_stem_values->use_100 = TRUE; ++ i = ALWAYS_USE_100_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ ++ i = 0; ++ while ( i < SNAPPING_SYNTHESIZE_STEMS_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_SYNTHESIZE_STEMS_Rules[i].family, ++ family ) == 0 ) ) ++ { ++ known_stem_values->synth_stems = FALSE; /* default */ ++ ++ if ( ppem >= SNAPPING_SYNTHESIZE_STEMS_Rules[i].ppem[0] && ++ ppem <= SNAPPING_SYNTHESIZE_STEMS_Rules[i].ppem[1] ) ++ { ++ known_stem_values->synth_stems = TRUE; ++ i = SNAPPING_SYNTHESIZE_STEMS_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ ++ i = 0; ++ while ( i < SNAPPING_EDGE_DETECTION_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_EDGE_DETECTION_Rules[i].family, ++ family ) == 0 ) ) ++ { ++ known_stem_values->edge_detection = FALSE; /* default */ ++ ++ if ( ppem >= SNAPPING_EDGE_DETECTION_Rules[i].ppem[0] && ++ ppem <= SNAPPING_EDGE_DETECTION_Rules[i].ppem[1] ) ++ { ++ known_stem_values->edge_detection = TRUE; ++ i = SNAPPING_EDGE_DETECTION_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ ++ i = 0; ++ while ( i < SNAPPING_NO_BEARING_CORRECTION_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( SNAPPING_NO_BEARING_CORRECTION_Rules[i].family, ++ family ) == 0 ) ) ++ { ++ known_stem_values->bearing_correction = TRUE; /* default */ ++ ++ if ( ppem >= SNAPPING_NO_BEARING_CORRECTION_Rules[i].ppem[0] && ++ ppem <= SNAPPING_NO_BEARING_CORRECTION_Rules[i].ppem[1] ) ++ { ++ known_stem_values->bearing_correction = FALSE; ++ i = SNAPPING_NO_BEARING_CORRECTION_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ ++#if 0 ++ i = 0; ++ while ( i < AUTOHINT_BRIGHTNESS_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( BRIGHTNESS_Rules[i].family, family ) == 0 ) ) ++ { ++ known_stem_values->brightness = 0.0; ++ ++ if ( ppem == BRIGHTNESS_Rules[i].ppem[0] || ++ BRIGHTNESS_Rules[i].ppem[0] == 0 ) ++ { ++ known_stem_values->brightness = BRIGHTNESS_Rules[i].ppem[1]; ++ i = AUTOHINT_BRIGHTNESS_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ i = 0; ++ while ( i < AUTOHINT_CONTRAST_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( CONTRAST_Rules[i].family, family ) == 0 ) ) ++ { ++ known_stem_values->contrast = 0.0; ++ ++ if ( ppem == CONTRAST_Rules[i].ppem[0] || ++ CONTRAST_Rules[i].ppem[0] == 0 ) ++ { ++ known_stem_values->contrast = CONTRAST_Rules[i].ppem[1]; ++ i = AUTOHINT_CONTRAST_RULES_SIZE; ++ } ++ } ++ i++; ++ } ++ ++ for ( i = 0; i <= STEM_SPACING_RULES_SIZE; i++ ) ++ { ++ if ( family && ++ ( strcasecmp( STEM_SPACING_Rules[i].family, family ) == 0 ) ) ++ { ++ j = 0; ++ known_stem_values->stem_spacing = 2; /* default */ ++ ++ while (j < 4) ++ { ++ if ( ppem < STEM_SPACING_Rules[i].ppem[j] ) ++ { ++ known_stem_values->stem_spacing = j; ++ j = 5; ++ } ++ j++; ++ } ++ } ++ } ++ ++ ++ for ( i = 0; i <= STEM_START_RULES_SIZE; i++ ) ++ { ++ if ( family && ++ ( strcasecmp( STEM_START_Rules[i].family, family ) == 0 ) ) ++ { ++ j = 0; ++ known_stem_values->stem_start = 1; /* default */ ++ ++ while (j < 4) ++ { ++ if ( ppem < STEM_START_Rules[i].ppem[j] ) ++ { ++ known_stem_values->stem_start = j; ++ j = 5; ++ } ++ j++; ++ } ++ } ++ } ++#endif ++ } ++ ++ ++ FT_LOCAL_DEF( FT_Int ) ++ get_contrast ( FT_String* family, ++ int ppem ) ++ { ++ FT_Int i; ++ ++ ++ if ( verbose ) ++ printf( "%s ", family ); ++ ++ i = 0; ++ while ( i < AUTOHINT_CONTRAST_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( CONTRAST_Rules[i].family, family ) == 0 ) ) ++ { ++ if ( ppem == CONTRAST_Rules[i].ppem[0] || ++ CONTRAST_Rules[i].ppem[0] == 0 ) ++ return CONTRAST_Rules[i].ppem[1]; ++ } ++ i++; ++ } ++ return 0; ++ } ++ ++ ++ FT_LOCAL_DEF( FT_Int ) ++ get_brightness ( FT_String* family, ++ int ppem ) ++ { ++ FT_Int i; ++ ++ ++ if ( verbose ) ++ printf("%s ", family); ++ ++ i = 0; ++ while ( i < AUTOHINT_BRIGHTNESS_RULES_SIZE ) ++ { ++ if ( family && ++ ( strcasecmp( BRIGHTNESS_Rules[i].family, family ) == 0 ) ) ++ { ++ if ( ppem == BRIGHTNESS_Rules[i].ppem[0] || ++ BRIGHTNESS_Rules[i].ppem[0] == 0 ) ++ return BRIGHTNESS_Rules[i].ppem[1]; ++ } ++ i++; ++ } ++ return 0; ++ } ++ ++ ++ /* 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[width * 256]; ++ 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;*/ ++ ++ FT_Bool has_serifs = FALSE; ++ FT_Bool autohinted = FALSE; ++ ++ const FT_Int MIN_PPEM = 7; ++ /*const FT_Int MAX_PPEM = 100;*/ ++ const FT_Int MAX_STEMS = 3; ++ FT_Int ppem = 0; ++ ++ FT_Bool checked_use_known_settings_on_selected_fonts_env = FALSE; ++ FT_Bool use_known_settings_on_selected_fonts = FALSE; ++ ++ FT_Pos cur_width = infinality_cur_width; ++ ++ ++ if ( cur_width ) ++ { ++ autohinted = TRUE; ++ } ++ ++ /* reset to default */ ++ *scale_value = 1.0; ++ ++ if ( !checked_use_known_settings_on_selected_fonts_env ) ++ { ++ char *use_known_settings_on_selected_fonts_env = ++ getenv( "INFINALITY_FT_USE_KNOWN_SETTINGS_ON_SELECTED_FONTS" ); ++ if ( use_known_settings_on_selected_fonts_env != NULL ) ++ { ++ if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "default" ) != 0 ) ++ { ++ if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "true") == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ else if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "1") == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ else if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "on") == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ else if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "yes") == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ } ++ } ++ checked_use_known_settings_on_selected_fonts_env = TRUE; ++ } ++ ++ ++ /* 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; ++ ++ ++ /* 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 ( slot->face->family_name ) ++ { ++ if ( strcasestr(slot->face->family_name, "Courier" ) || ++ strcasestr(slot->face->family_name, "Serif" ) || ++ strcasestr(slot->face->family_name, "Times" ) ) ++ has_serifs = TRUE; ++ } ++ ++ 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; ++ } ++ /* only look at top 3 for now */ ++ known_stem_values ++ = (Stem_Data*) malloc ( columns_per_pixel * sizeof ( Stem_Data ) ); ++ known_stem_values->stem_spacing = -1; ++ known_stem_values->stem_width = -1; ++ known_stem_values->stem_start = -1; ++ known_stem_values->stem_scaling = -1; ++ known_stem_values->stem_translating_only = -1024; ++ known_stem_values->stem_translating = 0; ++ known_stem_values->brightness = 0; ++ known_stem_values->contrast = 0; ++ known_stem_values->use_100 = FALSE; ++ known_stem_values->m = -1; ++ known_stem_values->synth_stems = FALSE; ++ known_stem_values->bearing_correction = TRUE; ++ ++ if ( use_known_settings_on_selected_fonts ) ++ { ++ sa_fill_known_stem_values ( slot->face->family_name, ++ ppem, slot->face->style_name, ++ valid_stems, known_stem_values ); ++ if ( verbose ) ++ printf ( "width:%d,spacing:%d,start:%d,scaling:%d,translate:%d ", ++ known_stem_values->stem_width, ++ known_stem_values->stem_spacing, ++ known_stem_values->stem_start, ++ known_stem_values->stem_scaling, ++ known_stem_values->stem_translating_only ); ++ } ++ ++ /* translate value may be set for < 10 */ ++ if ( use_known_settings_on_selected_fonts && ++ known_stem_values->stem_translating_only > -1024 ) ++ { ++ *translate_value = known_stem_values->stem_translating_only; ++ free ( known_stem_values ); ++ return; ++ } ++ ++ if ( use_known_settings_on_selected_fonts && ++ known_stem_values->bearing_correction == FALSE ) ++ strategy_bearing_correction = 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; ++ ++ if ( ppem < 9 ) ++ return; ++ if ( ppem > 20 ) ++ strategy_use_m_control = TRUE; ++ ++ /* Allocate */ ++ segments ++ = (Stem_Segment*) malloc( (1) * sizeof ( Stem_Segment ) ); ++ leftmost_segment ++ = (Stem_Segment*) malloc( sizeof ( Stem_Segment ) ); ++ leftmost_segment_not_extrema ++ = (Stem_Segment*) malloc( sizeof ( Stem_Segment ) ); ++ rightmost_segment ++ = (Stem_Segment*) malloc( sizeof ( Stem_Segment ) ); ++ rightmost_segment_not_extrema ++ = (Stem_Segment*) malloc( sizeof ( Stem_Segment ) ); ++ ++ stems = (Stem*) malloc ( MAX_STEMS * sizeof ( Stem ) ); ++ possible_stems = (Stem*) malloc ( MAX_STEMS * sizeof ( Stem ) ); ++ leftmost_stem = (Stem*) malloc ( sizeof (Stem)); ++ rightmost_stem = (Stem*) malloc ( sizeof(Stem)); ++ centers = (Stem_Center*) malloc ( (1) * sizeof ( Stem_Center ) ); ++ ++ if ( verbose ) ++ printf("\n"); ++ ++ /* Initialize */ ++ for ( xx = 0; xx < width * 256; xx += 1 ) ++ stem_centers[xx] = 0; ++ ++ for ( xx = 0; xx < num_segments; xx += 1 ) ++ { ++ segments[xx].x1 = 0; ++ segments[xx].x2 = 0; ++ segments[xx].y = 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 */ ++ segments = (Stem_Segment*) realloc ++ ( segments, ( num_segments + 1 ) * 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 */ ++ centers = (Stem_Center*) realloc ++ ( centers, ( num_centers + 1 ) * 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 best_height = 0, 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 large_gap_found = FALSE, no_gap_found = FALSE; ++ FT_Bool large_gap_found_ledge = FALSE, no_gap_found_ledge = FALSE; ++ FT_Bool large_gap_found_redge = FALSE, 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; ++ ++ large_gap_found = large_gap_found_ledge = large_gap_found_redge = FALSE; ++ 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 ) ++ large_gap_found = TRUE; ++ ++ /* 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 ) ++ large_gap_found_ledge = TRUE; ++ ++ /* 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 ) ++ large_gap_found_redge = TRUE; ++ ++ /* 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; ++ ++ best_height = 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; ++ best_height = stem_matches; ++ 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; ++ best_height = stem_matches_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; ++ best_height = stem_matches_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 ) ++ if ( 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] ); ++ ++ 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; ++ ++ ++ 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; ++ } ++ } ++ else ++ /* Start snapping */ ++ { ++ 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; ++ } ++ ++ Exit: ++ ++#define transformed_point( point ) point * *scale_value + *translate_value * 12 ++ ++ 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 ( segments ); ++ free ( leftmost_segment ); ++ free ( leftmost_segment_not_extrema ); ++ free ( rightmost_segment ); ++ free ( rightmost_segment_not_extrema ); ++ free ( known_stem_values ); ++ free ( stems ); ++ free ( possible_stems ); ++ free ( leftmost_stem ); ++ free ( rightmost_stem ); ++ free ( centers ); ++ } ++ ++ ++ /* Gamma correction */ ++ static void ++ _ft_lcd_gamma_correction_correction ( FT_Bitmap* bitmap, ++ FT_Render_Mode mode, ++ FT_GlyphSlot slot, ++ float gamma_correction_lt, ++ float gamma_correction_value ) ++ { ++ 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->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 ++ ft_smooth_render_generic( FT_Renderer render, ++ FT_GlyphSlot slot, ++ FT_Render_Mode mode, ++ const FT_Vector* origin, ++ FT_Render_Mode required_mode ) ++ { ++ FT_Error error; ++ FT_Outline* outline = NULL; ++ FT_Outline* outline_orig = NULL; ++ FT_BBox cbox; ++ 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_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 checked_chromeos_style_sharpening_strength = 0; ++ int alignment_strength = 0; ++ int fitting_strength = 0; ++ FT_UInt checked_alignment_strength = 0; ++ FT_UInt checked_fitting_strength = 0; ++ FT_UInt checked_fringe_filter_strength = 0; ++ int fringe_filter_strength = 0; ++ FT_UInt checked_grayscale_filter_strength = 0; ++ int grayscale_filter_strength = 0; ++ ++ FT_UInt checked_autohint_horizontal_stem_darken_strength = 0; ++ int autohint_horizontal_stem_darken_strength = 0; ++ ++ FT_UInt checked_autohint_vertical_stem_darken_strength = 0; ++ int autohint_vertical_stem_darken_strength = 0; ++ ++ int windows_style_sharpening_strength = 0; ++ FT_UInt checked_windows_style_sharpening_strength = 0; ++ float gamma_correction_value = 1; ++ float gamma_correction_lt = 0; ++ FT_UInt checked_gamma_correction_value = 0; ++ ++ FT_Int brightness_value = 0.0; ++ FT_UInt checked_brightness_value = 0; ++ ++ FT_Int contrast_value = 0.0; ++ FT_UInt checked_contrast_value = 0; ++ ++ FT_Int snapping_sliding_scale_value = 0; ++ FT_UInt checked_snapping_sliding_scale_value = 0; ++ ++ FT_Int global_embolden_x_value = 0; ++ FT_UInt checked_global_embolden_x_value = 0; ++ ++ FT_Int global_embolden_y_value = 0; ++ FT_UInt checked_global_embolden_y_value = 0; ++ ++ FT_Int bold_embolden_x_value = 0; ++ FT_UInt checked_bold_embolden_x_value = 0; ++ ++ FT_Int bold_embolden_y_value = 0; ++ FT_UInt checked_bold_embolden_y_value = 0; ++ ++ FT_Byte chromeos_cutoff; ++ double chromeos_gamma_value; ++ ++ float embolden_value = 0.0; ++ FT_Bool autohinted = FALSE; ++ ++ FT_UInt autohint_minimum_stem_height = 0; ++ FT_UInt checked_autohint_minimum_stem_height = 0; ++ ++ int checked_use_various_tweaks_env = 0; ++ 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; */ ++ ++ int checked_use_known_settings_on_selected_fonts_env = 0; ++ FT_Bool use_known_settings_on_selected_fonts = FALSE; ++ ++ 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 ( checked_use_known_settings_on_selected_fonts_env == 0 ) ++ { ++ char *use_known_settings_on_selected_fonts_env = ++ getenv( "INFINALITY_FT_USE_KNOWN_SETTINGS_ON_SELECTED_FONTS" ); ++ if ( use_known_settings_on_selected_fonts_env != NULL ) ++ { ++ if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "default" ) != 0 ) ++ { ++ if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "true" ) == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ else if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "1" ) == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ else if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "on" ) == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ else if ( strcasecmp( use_known_settings_on_selected_fonts_env, ++ "yes" ) == 0 ) ++ use_known_settings_on_selected_fonts = TRUE; ++ } ++ } ++ checked_use_known_settings_on_selected_fonts_env = 1; ++ } ++ ++ if ( checked_use_various_tweaks_env == 0 ) ++ { ++ char *use_various_tweaks_env ++ = getenv( "INFINALITY_FT_USE_VARIOUS_TWEAKS" ); ++ ++ if ( use_various_tweaks_env != NULL ) ++ { ++ if ( strcasecmp( use_various_tweaks_env, "default" ) != 0 ) ++ { ++ if ( strcasecmp( use_various_tweaks_env, "true") == 0) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp( use_various_tweaks_env, "1") == 0) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp( use_various_tweaks_env, "on") == 0) ++ use_various_tweaks = TRUE; ++ else if ( strcasecmp( use_various_tweaks_env, "yes") == 0) ++ use_various_tweaks = TRUE; ++ } ++ } ++ checked_use_various_tweaks_env = 1; ++ } ++ ++ if ( checked_autohint_minimum_stem_height == 0 ) ++ { ++ char *autohint_minimum_stem_height_env = ++ getenv( "INFINALITY_FT_AUTOHINT_MINIMUM_STEM_WIDTH" ); ++ ++ if ( autohint_minimum_stem_height_env != NULL ) ++ { ++ sscanf ( autohint_minimum_stem_height_env, "%u", ++ &autohint_minimum_stem_height ); ++ ++ if ( autohint_minimum_stem_height > 100 ) ++ autohint_minimum_stem_height = 100; ++ else if ( autohint_minimum_stem_height < 0 ) ++ autohint_minimum_stem_height = 0; ++ } ++ checked_autohint_minimum_stem_height = 1; ++ } ++ ++ if ( checked_snapping_sliding_scale_value == 0 ) ++ { ++ char *snapping_sliding_scale_env = ++ getenv ( "INFINALITY_FT_STEM_SNAPPING_SLIDING_SCALE" ); ++ ++ if ( snapping_sliding_scale_env != NULL ) ++ { ++ sscanf ( snapping_sliding_scale_env, "%d", ++ &snapping_sliding_scale_value ); ++ ++ if ( snapping_sliding_scale_value > MAX_PPEM ) ++ snapping_sliding_scale_value = 0; ++ else if ( snapping_sliding_scale_value < 0 ) ++ snapping_sliding_scale_value = 0; ++ ++ if (snapping_sliding_scale_value < 11 && ++ snapping_sliding_scale_value > 0 ) ++ snapping_sliding_scale_value = 11; ++ } ++ checked_snapping_sliding_scale_value = 1; ++ } ++ ++ if ( checked_alignment_strength == 0) ++ { ++ char *alignment_strength_env = ++ getenv ( "INFINALITY_FT_STEM_ALIGNMENT_STRENGTH" ); ++ ++ if ( alignment_strength_env != NULL ) ++ { ++ sscanf ( alignment_strength_env, "%d", &alignment_strength ); ++ ++ if ( alignment_strength > 100 ) ++ alignment_strength = 100; ++ else if ( alignment_strength < 0 ) ++ alignment_strength = 0; ++ } ++ ++ if ( alignment_strength > 100 ) ++ alignment_strength = 100; ++ checked_alignment_strength = 1; ++ ++ if ( snapping_sliding_scale_value != 0 ) ++ alignment_strength = sliding_scale ++ ( 10, snapping_sliding_scale_value, alignment_strength, 100, ppem ); ++ } ++ ++ if ( checked_fitting_strength == 0 ) ++ { ++ char *fitting_strength_env = ++ getenv( "INFINALITY_FT_STEM_FITTING_STRENGTH" ); ++ ++ if ( fitting_strength_env != NULL ) ++ { ++ sscanf ( fitting_strength_env, "%d", &fitting_strength ); ++ ++ if ( fitting_strength > 100 ) ++ fitting_strength = 100; ++ else if ( fitting_strength < 0 ) ++ fitting_strength = 0; ++ } ++ ++ if ( fitting_strength > 100 ) ++ fitting_strength = 100; ++ ++ checked_fitting_strength = 1; ++ ++ if ( snapping_sliding_scale_value != 0 ) ++ fitting_strength = sliding_scale ++ ( 10, snapping_sliding_scale_value, fitting_strength, 100, ppem ); ++ } ++ ++ if ( checked_chromeos_style_sharpening_strength == 0 ) ++ { ++ char *chromeos_style_sharpening_strength_env = ++ getenv( "INFINALITY_FT_CHROMEOS_STYLE_SHARPENING_STRENGTH" ); ++ if ( chromeos_style_sharpening_strength_env != NULL ) ++ { ++ sscanf ( chromeos_style_sharpening_strength_env, "%d", ++ &chromeos_style_sharpening_strength ); ++ ++ if ( chromeos_style_sharpening_strength > 100 ) ++ chromeos_style_sharpening_strength = 100; ++ else if ( chromeos_style_sharpening_strength < 0 ) ++ chromeos_style_sharpening_strength = 0; ++ } ++ ++ if ( ppem > 10 ) ++ chromeos_style_sharpening_strength = ++ ( chromeos_style_sharpening_strength * ppem ) / 10; ++ ++ if ( chromeos_style_sharpening_strength > 100 ) ++ chromeos_style_sharpening_strength = 100; ++ checked_chromeos_style_sharpening_strength = 1; ++ } ++ ++ ++ if ( checked_brightness_value == 0) ++ { ++ char *brightness_env = getenv( "INFINALITY_FT_BRIGHTNESS" ); ++ if ( brightness_env != NULL ) ++ { ++ sscanf ( brightness_env, "%d", &brightness_value ); ++ if (brightness_value > 100 ) ++ brightness_value = 100; ++ else if (brightness_value < -100 ) ++ brightness_value = 0; ++ } ++ checked_brightness_value = 1; ++ } ++ ++ if ( checked_contrast_value == 0) ++ { ++ char *contrast_env = getenv( "INFINALITY_FT_CONTRAST" ); ++ if ( contrast_env != NULL ) ++ { ++ sscanf ( contrast_env, "%d", &contrast_value ); ++ if (contrast_value > 100 ) ++ contrast_value = 100; ++ else if (contrast_value < -100 ) ++ contrast_value = 100; ++ } ++ checked_contrast_value = 1; ++ } + +- /* initialize renderer -- init its raster */ +- static FT_Error +- ft_smooth_init( FT_Renderer render ) +- { +- FT_Library library = FT_MODULE_LIBRARY( render ); ++ if ( checked_windows_style_sharpening_strength == 0) ++ { ++ char *windows_style_sharpening_strength_env = ++ getenv( "INFINALITY_FT_WINDOWS_STYLE_SHARPENING_STRENGTH" ); + ++ if ( windows_style_sharpening_strength_env != NULL ) ++ { ++ sscanf ( windows_style_sharpening_strength_env, "%d", ++ &windows_style_sharpening_strength ); + +- render->clazz->raster_class->raster_reset( render->raster, +- library->raster_pool, +- library->raster_pool_size ); ++ if ( windows_style_sharpening_strength > 100 ) ++ windows_style_sharpening_strength = 100; ++ else if ( windows_style_sharpening_strength < 0 ) ++ windows_style_sharpening_strength = 0; ++ } ++ /* 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; ++ checked_windows_style_sharpening_strength = 1; ++ } + +- return 0; +- } ++ if ( checked_gamma_correction_value == 0 ) ++ { ++ char *gamma_correction_value_env = ++ getenv( "INFINALITY_FT_GAMMA_CORRECTION" ); + ++ if ( gamma_correction_value_env != NULL ) ++ { ++ float f1, f2; + +- /* 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 ); +- } ++ if ( strcasecmp( gamma_correction_value_env, "default" ) != 0 ) ++ { ++ sscanf ( gamma_correction_value_env, "%f %f", &f1, &f2 ); ++ gamma_correction_lt = f1; ++ gamma_correction_value = f2 / 100.0; ++ } ++ if ( gamma_correction_value < .01 ) gamma_correction_value = 1.0; ++ } ++ checked_gamma_correction_value = 1; ++ } + +- /* 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; ++ /* 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 ( slot->format != render->glyph_format ) ++ if ( checked_fringe_filter_strength == 0 ) + { +- error = FT_THROW( Invalid_Argument ); +- goto Exit; ++ char *fringe_filter_strength_env = ++ getenv( "INFINALITY_FT_FRINGE_FILTER_STRENGTH" ); ++ if ( fringe_filter_strength_env != NULL ) ++ { ++ sscanf ( fringe_filter_strength_env, "%d", &fringe_filter_strength ); ++ ++ if ( fringe_filter_strength > 100 ) ++ fringe_filter_strength = 100; ++ else if ( fringe_filter_strength < 0 ) ++ fringe_filter_strength = 0; ++ } ++ checked_fringe_filter_strength = 1; + } + +- if ( matrix ) +- FT_Outline_Transform( &slot->outline, matrix ); + +- if ( delta ) +- FT_Outline_Translate( &slot->outline, delta->x, delta->y ); ++ if ( checked_grayscale_filter_strength == 0) ++ { ++ char *grayscale_filter_strength_env = ++ getenv( "INFINALITY_FT_GRAYSCALE_FILTER_STRENGTH" ); ++ if ( grayscale_filter_strength_env != NULL ) ++ { ++ sscanf ( grayscale_filter_strength_env, "%d", ++ &grayscale_filter_strength ); ++ if ( grayscale_filter_strength > 100 ) grayscale_filter_strength = 100; ++ else if (grayscale_filter_strength < 0 ) grayscale_filter_strength = 0; ++ } ++ checked_grayscale_filter_strength = 1; ++ } + +- Exit: +- return error; +- } + ++ if ( checked_autohint_horizontal_stem_darken_strength == 0) ++ { ++ char *autohint_horizontal_stem_darken_strength_env = ++ getenv( "INFINALITY_FT_AUTOHINT_HORIZONTAL_STEM_DARKEN_STRENGTH" ); ++ if ( autohint_horizontal_stem_darken_strength_env != NULL ) ++ { ++ sscanf ( autohint_horizontal_stem_darken_strength_env, "%d", ++ &autohint_horizontal_stem_darken_strength ); + +- /* 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 ( autohint_horizontal_stem_darken_strength > 100 ) ++ autohint_horizontal_stem_darken_strength = 100; ++ else if ( autohint_horizontal_stem_darken_strength < 0 ) ++ autohint_horizontal_stem_darken_strength = 0; ++ } ++ checked_autohint_horizontal_stem_darken_strength = 1; ++ } + +- if ( slot->format == render->glyph_format ) +- FT_Outline_Get_CBox( &slot->outline, cbox ); +- } ++ if ( checked_autohint_vertical_stem_darken_strength == 0) ++ { ++ char *autohint_vertical_stem_darken_strength_env = ++ getenv( "INFINALITY_FT_AUTOHINT_VERTICAL_STEM_DARKEN_STRENGTH" ); ++ if ( autohint_vertical_stem_darken_strength_env != NULL ) ++ { ++ sscanf ( autohint_vertical_stem_darken_strength_env, "%d", ++ &autohint_vertical_stem_darken_strength ); + ++ if ( autohint_vertical_stem_darken_strength > 100 ) ++ autohint_vertical_stem_darken_strength = 100; ++ else if ( autohint_horizontal_stem_darken_strength < 0 ) ++ autohint_vertical_stem_darken_strength = 0; ++ } ++ checked_autohint_vertical_stem_darken_strength = 1; ++ } + +- /* convert a slot's glyph image into a bitmap */ +- static FT_Error +- ft_smooth_render_generic( FT_Renderer render, +- FT_GlyphSlot slot, +- FT_Render_Mode mode, +- const FT_Vector* origin, +- 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_BBox cbox; +- FT_Pos x_shift = 0; +- FT_Pos y_shift = 0; +- FT_Pos x_left, y_top; +- FT_Pos width, height, pitch; +-#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING +- FT_Pos height_org, width_org; +-#endif +- FT_Int hmul = mode == FT_RENDER_MODE_LCD; +- FT_Int vmul = mode == FT_RENDER_MODE_LCD_V; ++ if ( checked_global_embolden_x_value == 0) ++ { ++ char *global_embolden_x_env = ++ getenv ( "INFINALITY_FT_GLOBAL_EMBOLDEN_X_VALUE" ); ++ if ( global_embolden_x_env != NULL ) ++ { ++ sscanf ( global_embolden_x_env, "%d", &global_embolden_x_value ); + +- FT_Raster_Params params; ++ if ( global_embolden_x_value > 128 ) ++ global_embolden_x_value = 128; ++ else if ( global_embolden_x_value < -128 ) ++ global_embolden_x_value = -128; ++ } ++ checked_global_embolden_x_value = 1; ++ } ++ ++ if ( checked_global_embolden_y_value == 0) ++ { ++ char *global_embolden_y_env = ++ getenv ( "INFINALITY_FT_GLOBAL_EMBOLDEN_Y_VALUE" ); ++ if ( global_embolden_y_env != NULL ) ++ { ++ sscanf ( global_embolden_y_env, "%d", &global_embolden_y_value ); ++ if ( global_embolden_y_value > 128 ) ++ global_embolden_y_value = 128; ++ else if ( global_embolden_y_value < -128 ) ++ global_embolden_y_value = -128; ++ } ++ checked_global_embolden_y_value = 1; ++ } ++ ++ ++ if ( checked_bold_embolden_x_value == 0) ++ { ++ char *bold_embolden_x_env = ++ getenv ( "INFINALITY_FT_BOLD_EMBOLDEN_X_VALUE" ); ++ ++ if ( bold_embolden_x_env != NULL ) ++ { ++ sscanf ( bold_embolden_x_env, "%d", &bold_embolden_x_value ); ++ if (bold_embolden_x_value > 128 ) ++ bold_embolden_x_value = 128; ++ else if (bold_embolden_x_value < -128 ) ++ bold_embolden_x_value = -128; ++ } ++ checked_bold_embolden_x_value = 1; ++ } ++ ++ if ( checked_bold_embolden_y_value == 0) ++ { ++ char *bold_embolden_y_env = getenv ( "INFINALITY_FT_BOLD_EMBOLDEN_Y_VALUE" ); ++ ++ if ( bold_embolden_y_env != NULL ) ++ { ++ sscanf ( bold_embolden_y_env, "%d", &bold_embolden_y_value ); ++ if (bold_embolden_y_value > 128 ) ++ bold_embolden_y_value = 128; ++ else if (bold_embolden_y_value < -128 ) ++ bold_embolden_y_value = -128; ++ } ++ checked_bold_embolden_y_value = 1; ++ } + +- FT_Bool have_outline_shifted = FALSE; +- FT_Bool have_buffer = FALSE; ++ 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 +3610,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 +3732,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 +3764,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,25 +3793,6 @@ + goto Exit; + } + +- /* 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 */ +- if ( FT_ALLOC( bitmap->buffer, (FT_ULong)( pitch * height ) ) ) +- goto Exit; +- else +- have_buffer = TRUE; +- +- 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; +@@ -248,11 +3800,22 @@ + bitmap->pitch = pitch; + + /* translate outline to render it into the bitmap */ +- if ( x_shift || y_shift ) ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET ++ if ( align_called == 0 ) + { +- FT_Outline_Translate( outline, x_shift, y_shift ); +- have_outline_shifted = TRUE; ++#endif ++ FT_Outline_Translate( outline, -x_shift, -y_shift ); ++ have_outline_shifted = TRUE; ++#ifdef FT_CONFIG_OPTION_INFINALITY_PATCHSET + } ++#endif ++ ++ if ( FT_ALLOC( bitmap->buffer, (FT_ULong)( pitch * height ) ) ) ++ goto Exit; ++ else ++ have_buffer = TRUE; ++ ++ slot->internal->flags |= FT_GLYPH_OWN_BITMAP; + + /* set up parameters */ + params.target = bitmap; +@@ -299,9 +3862,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 */ ++ if ( slot->face && !_ft_bitmap_bc ( bitmap, ++ (float)get_brightness( slot->face->family_name, ppem ) / 300.0, ++ (float)get_contrast( slot->face->family_name, ppem ) / 300.0 ) ) ++ { ++ 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 +4066,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 +4087,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 d00a0f8..da05b5a 100644 +--- a/src/truetype/ttinterp.c ++++ b/src/truetype/ttinterp.c +@@ -5801,6 +5801,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; + } |