Infinality Freetype Truetype Subpixel Hinting Patch ------------------------------------------------------------------- Should patch cleanly to freetype-2.4.3 Changes for 2010-11-14: * Rule tweaks on various fonts. Fixed the Cyrillic y issue and e issue with Trebuchet, and the ^ issue with Arial. Other issues (firefox and @font-face) are still present to a degree. * A couple new rules to deal with various issues. (work in progress) * Additional commenting. * Some cleanup of obsolete code. * Added some debugging code for potential future enhancements. Please ignore the mess. Changes for 2010-10-22: * I'm refocusing on just getting the subpixel looking nice, so I've stripped back the rendering modes to just 2. The standard SUBPIXEL_HINTING and the ADDITIONAL_TWEAKS. The rules structure is still in place. I recommend using ADDITIONAL_TWEAKS mode. * Fixed an issue with monochrome rendering that made fonts look really bad. There is still an issue with them, but they are at least tolerable to look at now. * Added some testing code for detecting inline delta functions. Not sure if this is useful yet. * Added more rules to deal with certain artifacts on various fonts, like the issue with < > and ^. Created some "exception" rules for certain rules. * Reverted back to older rounding functions. The new experimental ones I was trying were causing artifacts on some fonts. * Some code cleanup. Changes for 2010-10-08: * Fix PDF crashes. Changes for 2010-10-04: * Update to freetype-2.4.3 Changes for 2010-10-03: * There are lots of changes for this one, some big, some small, and some that still are not implemented. Not sure if I can remember them all but I will try! THIS IS A POINT RELEASE THAT IS NOT INTENDED TO WORK 100%. Some fonts and compile options may be broken and the code may be inefficient and/or not syntactiacally correct. That said, I do plan on using this on my system right away. * There are now "rendering modes" for the subpixel hinting, with the idea that this will enventually be able to be controlled by fontconfig. The 4 modes of enhanced hinting defined so far are: 1) NATIVE HINTING - this is what freetype TT interpreter does by default. 2) FIXED NATIVE HINTING - A slighly tweaked version of the above that does "better" native rendering when displaying on LCD, for those that still seem to like incorrect, thin fonts, which were only ever there due to technical limitations. 3) SUBPIXEL OPTIMIZED HINTING - this is straight up subpixel hinting with very few tweaks. Just enough to get it working. 4) COMPATIBILITY MODE HINTING - this is the sweet spot I'm working on that will hopefully supplant #3 because it will work so well with all fonts. The idea here is to tweak all available fonts so that each renders well. All of these modes either turn on or off switches in the interpreter to make the fonts render properly for each mode. Right now these are only compile-time options. * Subpixel-related code has been broken out into its own files, so as to not clutter up the existing code. * The rasterizer now pays attention to the additional bits of MS rasterizer v. 37, meaning that it can now indicate to fonts that it can handle subpixel rendering. * The rounding functions have been adapted to accept a grid resolution variable, which lets them work on pixel and subpixel boundaries automatically. Y still needs to be implemented. * Additional conditions have been added to the switches, to further refine how they are applied to different fonts. * What all this means qualitatively is that legacy fonts now render much better. There are still some that need a bit of love, like Courier New. - Courier New has some fixes, and some breakage (Ghost pixels above bold fonts, too thin on regular font) - Times New Roman has some fixes and breakage (serifs, particularly) - Tahoma and Trebuchet MS have been cleaned up - Arial now snaps to grid better, but that causes breakage on a few glyphs - Verdana 13 is now set to grid fit, but some glyhs are broken (mwxyz) - Geneva and Geneva CY no longer look like turds - Lucida Sans Unicode now looks arguably better than Lucida Grande Changes for 2010-09-16: * The changes from 2010-09-14 regarding subpixel when LIGHT hinting enabled have been reverted due to problems. The old behavior is back. * Disable grayscale when subpixel is enabled. This results in better behavior of some TT instructions within some fonts, like Times New Roman. * Some modification of the tweaks, in light of above. Changes for 2010-09-14: /************************** NO LONGER IN PLACE *****************************/ * Subpixel hinting is now used when the LIGHT hinting method and the TT hinting is called. If FULL hinting is requested it will do the usual behavior of the TT hinter. This allows for all previously existing behavior, plus the new subpixel hinting behavior, all in the same compile, and it makes sense in that the slight hinting of the autohinter is essentially doing the same thing as this, which is not forcing X-direction hints. Previously, even if TT was selected, but LIGHT hinting was used, the autohinter would still be forced. Other than this, autohint is not affected. /***************************************************************************/ * Added a couple more conditionals around things to test whether subpixel hinting is enabled. There were a few missing that ended up causing some goofy hinting if subpixel was not enabled, but compiled in. diff -Nur freetype-2.4.3.orig/include/freetype/config/ftoption.h freetype-2.4.3.new/include/freetype/config/ftoption.h --- freetype-2.4.3.orig/include/freetype/config/ftoption.h 2010-07-04 23:38:55.000000000 -0500 +++ freetype-2.4.3.new/include/freetype/config/ftoption.h 2010-10-23 21:47:20.476211579 -0500 @@ -501,6 +501,48 @@ /*************************************************************************/ /* */ + /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ + /* EXPERIMENTAL subpixel hinting support into the TrueType driver. */ + /* This will replace the native TrueType hinting mechanism when */ + /* anything but FT_RENDER_MODE_MONO is requested. */ + /* */ + /* Enabling this causes the TrueType driver to ignore instructions */ + /* under certain conditions. This is done in accordance with the */ + /* guide here, with some minor differences: */ + /* */ + /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ + /* */ + /* By undefining this, you will only compile the code necessary to */ + /* hint TrueType glyphs with native TT hinting. */ + /* */ + /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ + /* defined. */ + /* */ +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + + /*************************************************************************/ + /* */ + /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS if you */ + /* want to enable additional subpixel hinting tweaks of individual fonts,*/ + /* glyphs, styles and sizes. The idea here is that some glyphs and */ + /* fonts still do not render in a desirable way with */ + /* TT_CONFIG_OPTION_SUBPIXEL_HINTING. */ + /* */ + /* This is disabled by default, as some people may not care, or may not */ + /* want the additional overhead involved in doing this. */ + /* */ + /* By undefining this, you will only compile the code necessary to */ + /* do subpixel hinting as defined above. */ + /* */ + /* This option requires TT_CONFIG_OPTION_SUBPIXEL_HINTING to be */ + /* defined. */ + /* */ +/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS */ + + + /*************************************************************************/ + /* */ /* 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 -Nur freetype-2.4.3.orig/include/freetype/internal/ftobjs.h freetype-2.4.3.new/include/freetype/internal/ftobjs.h --- freetype-2.4.3.orig/include/freetype/internal/ftobjs.h 2010-07-18 11:07:31.000000000 -0500 +++ freetype-2.4.3.new/include/freetype/internal/ftobjs.h 2010-11-07 11:18:19.681501081 -0600 @@ -81,6 +81,14 @@ #define FT_PIX_ROUND( x ) FT_PIX_FLOOR( (x) + 32 ) #define FT_PIX_CEIL( x ) FT_PIX_FLOOR( (x) + 63 ) + /* + * These are used in ttinterp.c for subpixel hinting with an + * adjustable grids-per-pixel value. + */ +#define FT_PIX_FLOOR_GRID( x, n ) ( (x) & ~(64 / n - 1) ) +#define FT_PIX_ROUND_GRID( x, n ) FT_PIX_FLOOR_GRID( (x) + 32 / n, n) +#define FT_PIX_CEIL_GRID( x, n ) FT_PIX_FLOOR_GRID( (x) + 63 / n, n) + /* * Return the highest power of 2 that is <= value; this correspond to diff -Nur freetype-2.4.3.orig/src/truetype/rules.mk freetype-2.4.3.new/src/truetype/rules.mk --- freetype-2.4.3.orig/src/truetype/rules.mk 2009-03-14 08:45:26.000000000 -0500 +++ freetype-2.4.3.new/src/truetype/rules.mk 2010-10-22 19:25:46.060977607 -0500 @@ -30,7 +30,8 @@ $(TT_DIR)/ttgload.c \ $(TT_DIR)/ttinterp.c \ $(TT_DIR)/ttgxvar.c \ - $(TT_DIR)/ttdriver.c + $(TT_DIR)/ttdriver.c \ + $(TT_DIR)/ttsubpixel.c # TrueType driver headers # diff -Nur freetype-2.4.3.orig/src/truetype/truetype.c freetype-2.4.3.new/src/truetype/truetype.c --- freetype-2.4.3.orig/src/truetype/truetype.c 2009-07-03 08:28:24.000000000 -0500 +++ freetype-2.4.3.new/src/truetype/truetype.c 2010-10-22 19:25:46.061853066 -0500 @@ -27,6 +27,7 @@ #ifdef TT_USE_BYTECODE_INTERPRETER #include "ttinterp.c" +#include "ttsubpixel.c" #endif #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT diff -Nur freetype-2.4.3.orig/src/truetype/ttgload.c freetype-2.4.3.new/src/truetype/ttgload.c --- freetype-2.4.3.orig/src/truetype/ttgload.c 2010-09-14 02:00:35.000000000 -0500 +++ freetype-2.4.3.new/src/truetype/ttgload.c 2010-11-14 09:07:50.835981062 -0600 @@ -33,7 +33,7 @@ #endif #include "tterrors.h" - +#include "ttsubpixel.h" /*************************************************************************/ /* */ @@ -166,6 +166,12 @@ loader->top_bearing = top_bearing; loader->vadvance = advance_height; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( loader->exec ) loader->exec->sph_tweak_flags = 0x00000; + /* this may not be the right place for this, but it works */ + if ( loader->exec && loader->exec->enhanced ) sph_set_tweaks( loader, glyph_index ); +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + if ( !loader->linear_def ) { loader->linear_def = 1; @@ -1683,13 +1689,23 @@ IS_HINTED( loader->load_flags ) ) { FT_Byte* widthp; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Bool enhanced; + enhanced = + FT_BOOL( FT_LOAD_TARGET_MODE( loader->load_flags ) + != FT_RENDER_MODE_MONO ); +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ widthp = tt_face_get_device_metrics( face, size->root.metrics.x_ppem, glyph_index ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( ( !enhanced || BITMAP_WIDTHS ) && widthp ) +#else if ( widthp ) +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ glyph->metrics.horiAdvance = *widthp << 6; } @@ -1883,8 +1899,13 @@ { TT_ExecContext exec; FT_Bool grayscale; - - +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Bool subpixel_hinting; + FT_Bool grayscale_hinting; + /*FT_Bool compatible_widths; + FT_Bool symmetrical_smoothing; + FT_Bool bgr;*/ +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ if ( !size->cvt_ready ) { FT_Error error = tt_size_ready_bytecode( size ); @@ -1898,20 +1919,75 @@ if ( !exec ) return TT_Err_Could_Not_Find_Context; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + subpixel_hinting = + FT_BOOL( (FT_LOAD_TARGET_MODE( load_flags ) != FT_RENDER_MODE_MONO) + && SET_SUBPIXEL ); + + if ( subpixel_hinting ) grayscale = grayscale_hinting = FALSE; + else if ( SET_GRAYSCALE ) + { + grayscale = grayscale_hinting = TRUE; + subpixel_hinting = FALSE; + } + + exec->enhanced = ( subpixel_hinting + || grayscale_hinting ); + + exec->rasterizer_version = SET_RASTERIZER_VERSION; + + exec->compatible_widths = SET_COMPATIBLE_WIDTHS; + /*FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) + != TT_LOAD_COMPATIBLE_WIDTHS );*/ + + exec->symmetrical_smoothing = FALSE; + /*FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) + != TT_LOAD_SYMMETRICAL_SMOOTHING );*/ + + exec->bgr = FALSE; + /*FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) + != TT_LOAD_BGR );*/ +#else grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != FT_RENDER_MODE_MONO ); +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ TT_Load_Context( exec, face, size ); - /* a change from mono to grayscale rendering (and vice versa) */ +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + + /* a change from mono to subpixel rendering (and vice versa) */ /* requires a re-execution of the CVT program */ - if ( grayscale != exec->grayscale ) + if ( subpixel_hinting != exec->subpixel_hinting ) + { + FT_UInt i; + + exec->subpixel_hinting = subpixel_hinting; + + for ( i = 0; i < size->cvt_size; i++ ) + size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale ); + tt_size_run_prep( size ); + } + + /* a change from mono to grayscale rendering (and vice versa) */ + /* requires a re-execution of the CVT program */ + if ( grayscale != exec->grayscale_hinting ) { FT_UInt i; + exec->grayscale_hinting = grayscale_hinting; - FT_TRACE4(( "tt_loader_init: grayscale change," - " re-executing `prep' table\n" )); + for ( i = 0; i < size->cvt_size; i++ ) + size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale ); + tt_size_run_prep( size ); + } +#else + + /* a change from mono to grayscale rendering (and vice versa) */ + /* requires a re-execution of the CVT program */ + if ( grayscale != exec->grayscale ) + { + FT_UInt i; exec->grayscale = grayscale; @@ -1919,6 +1995,7 @@ size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale ); tt_size_run_prep( size ); } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ /* see whether the cvt program has disabled hinting */ if ( exec->GS.instruct_control & 1 ) @@ -2050,6 +2127,7 @@ if ( face->postscript.isFixedPitch && ( load_flags & FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ) == 0 ) glyph->linearHoriAdvance = face->horizontal.advance_Width_Max; + } return TT_Err_Ok; @@ -2125,6 +2203,9 @@ } else glyph->outline.flags |= FT_OUTLINE_IGNORE_DROPOUTS; + + + } #endif /* TT_USE_BYTECODE_INTERPRETER */ diff -Nur freetype-2.4.3.orig/src/truetype/ttinterp.c freetype-2.4.3.new/src/truetype/ttinterp.c --- freetype-2.4.3.orig/src/truetype/ttinterp.c 2010-10-01 01:08:19.000000000 -0500 +++ freetype-2.4.3.new/src/truetype/ttinterp.c 2010-11-14 09:25:21.736322597 -0600 @@ -27,10 +27,12 @@ #include "tterrors.h" +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#include "ttsubpixel.h" +#endif #ifdef TT_USE_BYTECODE_INTERPRETER - #define TT_MULFIX FT_MulFix #define TT_MULDIV FT_MulDiv #define TT_MULDIV_NO_ROUND FT_MulDiv_No_Round @@ -150,11 +152,11 @@ #define NORMalize( x, y, v ) \ Normalize( EXEC_ARG_ x, y, v ) -#define SET_SuperRound( scale, flags ) \ - SetSuperRound( EXEC_ARG_ scale, flags ) +#define SET_SuperRound( scale, flags, res ) \ + SetSuperRound( EXEC_ARG_ scale, flags, res ) -#define ROUND_None( d, c ) \ - Round_None( EXEC_ARG_ d, c ) +#define ROUND_None( d, c, e ) \ + Round_None( EXEC_ARG_ d, c, e ) #define INS_Goto_CodeRange( range, ip ) \ Ins_Goto_CodeRange( EXEC_ARG_ range, ip ) @@ -165,8 +167,8 @@ #define CUR_Func_move_orig( z, p, d ) \ CUR.func_move_orig( EXEC_ARG_ z, p, d ) -#define CUR_Func_round( d, c ) \ - CUR.func_round( EXEC_ARG_ d, c ) +#define CUR_Func_round( d, c, e ) \ + CUR.func_round( EXEC_ARG_ d, c, e ) #define CUR_Func_read_cvt( index ) \ CUR.func_read_cvt( EXEC_ARG_ index ) @@ -246,6 +248,12 @@ #define GUESS_VECTOR( V ) #endif + + /*FT_Int CUR.num_delta_funcs;*/ + /*FT_ULong inline_delta_funcs[5];*/ + /*FT_Long CUR.infunc;*/ + + /*************************************************************************/ /* */ /* CODERANGE FUNCTIONS */ @@ -1838,24 +1846,33 @@ FT_ASSERT( !CUR.face->unpatented_hinting ); #endif - v = CUR.GS.freeVector.x; - - if ( v != 0 ) +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( !CUR.enhanced || CUR.sph_tweak_flags & SPH_TWEAK_ALLOW_DMOVE_FREEV ) { - zone->cur[point].x += TT_MULDIV( distance, +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + v = CUR.GS.freeVector.x; + + if ( v != 0 ) + { + zone->cur[point].x += TT_MULDIV( distance, v * 0x10000L, CUR.F_dot_P ); - zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; + zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; + } + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ v = CUR.GS.freeVector.y; if ( v != 0 ) { zone->cur[point].y += TT_MULDIV( distance, - v * 0x10000L, - CUR.F_dot_P ); + v * 0x10000L, + CUR.F_dot_P ); zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; } @@ -1895,18 +1912,17 @@ if ( v != 0 ) zone->org[point].x += TT_MULDIV( distance, - v * 0x10000L, - CUR.F_dot_P ); + v * 0x10000L, + CUR.F_dot_P ); v = CUR.GS.freeVector.y; if ( v != 0 ) zone->org[point].y += TT_MULDIV( distance, - v * 0x10000L, - CUR.F_dot_P ); + v * 0x10000L, + CUR.F_dot_P ); } - /*************************************************************************/ /* */ /* Special versions of Direct_Move() */ @@ -1923,9 +1939,16 @@ FT_F26Dot6 distance ) { FT_UNUSED_EXEC; - - zone->cur[point].x += distance; - zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( !CUR.enhanced + || ( CUR.sph_tweak_flags & SPH_TWEAK_ALLOW_DMOVEX_FREEV ) ) + { +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + zone->cur[point].x += distance; + zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ } @@ -1940,7 +1963,6 @@ zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; } - /*************************************************************************/ /* */ /* Special versions of Direct_Move_Orig() */ @@ -1959,6 +1981,7 @@ FT_UNUSED_EXEC; zone->org[point].x += distance; + } @@ -1969,7 +1992,7 @@ { FT_UNUSED_EXEC; - zone->org[point].y += distance; + zone->org[point].y += distance; } @@ -1997,7 +2020,8 @@ /* */ static FT_F26Dot6 Round_None( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; @@ -2016,6 +2040,7 @@ if ( val > 0 ) val = 0; } + return val; } @@ -2038,7 +2063,8 @@ /* */ static FT_F26Dot6 Round_To_Grid( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; @@ -2047,15 +2073,15 @@ if ( distance >= 0 ) { - val = distance + compensation + 32; + val = distance + compensation + 32 / resolution; if ( distance && val > 0 ) - val &= ~63; + val &= ~(64 / resolution - 1); else val = 0; } else { - val = -FT_PIX_ROUND( compensation - distance ); + val = -FT_PIX_ROUND_GRID( compensation - distance, resolution ); if ( val > 0 ) val = 0; } @@ -2082,22 +2108,22 @@ /* */ static FT_F26Dot6 Round_To_Half_Grid( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; FT_UNUSED_EXEC; - if ( distance >= 0 ) { - val = FT_PIX_FLOOR( distance + compensation ) + 32; + val = FT_PIX_FLOOR_GRID( distance + compensation, resolution ) + 32 / resolution; if ( distance && val < 0 ) val = 0; } else { - val = -( FT_PIX_FLOOR( compensation - distance ) + 32 ); + val = -( FT_PIX_FLOOR_GRID( compensation - distance, resolution ) + 32 / resolution ); if ( val > 0 ) val = 0; } @@ -2124,7 +2150,8 @@ /* */ static FT_F26Dot6 Round_Down_To_Grid( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; @@ -2135,13 +2162,13 @@ { val = distance + compensation; if ( distance && val > 0 ) - val &= ~63; + val &= ~(64 / resolution - 1 ); else val = 0; } else { - val = -( ( compensation - distance ) & -64 ); + val = -( ( compensation - distance ) & -(64 / resolution) ); if ( val > 0 ) val = 0; } @@ -2168,7 +2195,8 @@ /* */ static FT_F26Dot6 Round_Up_To_Grid( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; @@ -2177,15 +2205,15 @@ if ( distance >= 0 ) { - val = distance + compensation + 63; + val = distance + compensation + (64 / resolution - 1); if ( distance && val > 0 ) - val &= ~63; + val &= ~(64 / resolution - 1); else val = 0; } else { - val = - FT_PIX_CEIL( compensation - distance ); + val = - FT_PIX_CEIL_GRID( compensation - distance, resolution ); if ( val > 0 ) val = 0; } @@ -2212,24 +2240,26 @@ /* */ static FT_F26Dot6 Round_To_Double_Grid( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; FT_UNUSED_EXEC; - if ( distance >= 0 ) + if ( distance >= 0 ) { - val = distance + compensation + 16; + val = distance + compensation + 16 / resolution ; if ( distance && val > 0 ) - val &= ~31; + val &= ~(32 / resolution - 1); else val = 0; } else { - val = -FT_PAD_ROUND( compensation - distance, 32 ); + + val = -FT_PAD_ROUND( compensation - distance, 32 / resolution ); if ( val > 0 ) val = 0; } @@ -2262,7 +2292,8 @@ /* */ static FT_F26Dot6 Round_Super( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; @@ -2310,7 +2341,8 @@ /* */ static FT_F26Dot6 Round_Super_45( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ) + FT_F26Dot6 compensation, + FT_Int resolution ) { FT_F26Dot6 val; @@ -2401,7 +2433,8 @@ /* */ static void SetSuperRound( EXEC_OP_ FT_F26Dot6 GridPeriod, - FT_Long selector ) + FT_Long selector, + FT_Int resolution ) { switch ( (FT_Int)( selector & 0xC0 ) ) { @@ -3061,13 +3094,13 @@ #define DO_SROUND \ - SET_SuperRound( 0x4000, args[0] ); \ + SET_SuperRound( 0x4000, args[0], 1 ); \ CUR.GS.round_state = TT_Round_Super; \ CUR.func_round = (TT_Round_Func)Round_Super; #define DO_S45ROUND \ - SET_SuperRound( 0x2D41, args[0] ); \ + SET_SuperRound( 0x2D41, args[0], 1 ); \ CUR.GS.round_state = TT_Round_Super_45; \ CUR.func_round = (TT_Round_Func)Round_Super_45; @@ -3228,11 +3261,11 @@ #define DO_ODD \ - args[0] = ( ( CUR_Func_round( args[0], 0 ) & 127 ) == 64 ); + args[0] = ( ( CUR_Func_round( args[0], 0, 1 ) & 127 ) == 64 ); #define DO_EVEN \ - args[0] = ( ( CUR_Func_round( args[0], 0 ) & 127 ) == 0 ); + args[0] = ( ( CUR_Func_round( args[0], 0, 1 ) & 127 ) == 0 ); #define DO_AND \ @@ -3281,7 +3314,31 @@ #define DO_CEILING \ args[0] = FT_PIX_CEIL( args[0] ); - +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#define DO_RS \ + { \ + FT_ULong I = (FT_ULong)args[0]; \ + \ + \ + if ( BOUNDSL( I, CUR.storeSize ) ) \ + { \ + if ( CUR.pedantic_hinting ) \ + { \ + ARRAY_BOUND_ERROR; \ + } \ + else \ + args[0] = 0; \ + } \ + else \ + /* Subpixel Hinting - Avoid Typeman Dstroke and Istroke \ + * and Vacuform Rounds */ \ + if ( CUR.enhanced \ + && ( I == 24 || I == 22 || I == 8 ) \ + && !( CUR.sph_tweak_flags & SPH_TWEAK_DO_RS ) ) \ + args[0] = 0; \ + else args[0] = CUR.storage[I]; \ + } +#else #define DO_RS \ { \ FT_ULong I = (FT_ULong)args[0]; \ @@ -3299,6 +3356,7 @@ else \ args[0] = CUR.storage[I]; \ } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ #define DO_WS \ @@ -3378,12 +3436,12 @@ #define DO_ROUND \ args[0] = CUR_Func_round( \ args[0], \ - CUR.tt_metrics.compensations[CUR.opcode - 0x68] ); + CUR.tt_metrics.compensations[CUR.opcode - 0x68], 1 ); #define DO_NROUND \ args[0] = ROUND_None( args[0], \ - CUR.tt_metrics.compensations[CUR.opcode - 0x6C] ); + CUR.tt_metrics.compensations[CUR.opcode - 0x6C], 1 ); #define DO_MAX \ @@ -4554,7 +4612,21 @@ FT_ULong n; TT_DefRecord* rec; TT_DefRecord* limit; - +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + int opcode_pattern[4][12]= { + /* VacuFormRound function */ + {0x45,0x23,0x46,0x60,0x20}, + /* inline delta function 1 */ + {0x4B,0x53,0x23,0x4B,0x51,0x5A,0x58,0x38,0x1B,0x21,0x21,0x59}, + /* inline delta function 2 */ + {0x4B,0x54,0x58,0x38,0x1B,0x5A,0x21,0x21,0x59}, + /* diagonal stroke function */ + {0x20,0x20,0x40,0x60,0x47,0x40,0x23,0x42}, + }; + int opcode_patterns = 4; + int i; + int opcode_pointer[4] = {0,0,0,0} ; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ /* some font programs are broken enough to redefine functions! */ /* We will then parse the current table. */ @@ -4592,6 +4664,7 @@ rec->opc = (FT_UInt16)n; rec->start = CUR.IP + 1; rec->active = TRUE; + rec->inline = FALSE; if ( n > CUR.maxFunc ) CUR.maxFunc = (FT_UInt16)n; @@ -4601,20 +4674,63 @@ while ( SKIP_Code() == SUCCESS ) { + /*printf ("%d ", CUR.opcode);*/ +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + for ( i = 0; i < opcode_patterns; i++ ){ + if ( CUR.opcode == opcode_pattern[i][opcode_pointer[i]] ) + { + /*printf ("function %d, opcode ptrn: %d op# %d: %d FOUND -------------\n ", n, i, opcode_pointer[i], CUR.opcode);*/ + opcode_pointer[i] += 1; + + if (i == 0 && opcode_pointer[0] == 5){ + /*inline_delta_funcs[CUR.num_delta_funcs] = n; + CUR.num_delta_funcs++;*/ + /*printf ("Vacuform Round FUNCTION %d detected \n ", n);*/ + if (CUR.enhanced) /*rec->active = FALSE;*/ + opcode_pointer[i] = 0; + } + if (i == 1 && opcode_pointer[1] == 12){ + /*rec->active = FALSE;*/ + /*CUR.inline_delta_funcs[CUR.num_delta_funcs] = n; + CUR.num_delta_funcs++;*/ + rec->inline = TRUE; + /*printf ("inline delta FUNCTION1 %d detected \n ", n, CUR.num_delta_funcs);*/ + opcode_pointer[i] = 0; + } + if (i == 2 && opcode_pointer[1] == 9){ + /* CUR.inline_delta_funcs[CUR.num_delta_funcs] = n; + CUR.num_delta_funcs++;*/ + rec->inline = TRUE; + /*printf ("inline delta2 FUNCTION2 %d detected \n ", n, CUR.num_delta_funcs);*/ + opcode_pointer[i] = 0; + } + if (i == 4 && opcode_pointer[1] == 8){ + /* CUR.inline_delta_funcs[CUR.num_delta_funcs] = n; + CUR.num_delta_funcs++;*/ + /*rec->inline = TRUE;*/ + /*printf ("diagonal stroke function %d detected \n ", n, CUR.num_delta_funcs);*/ + opcode_pointer[i] = 0; + } + } + else { + opcode_pointer[i]=0; + } + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + switch ( CUR.opcode ) { - case 0x89: /* IDEF */ - case 0x2C: /* FDEF */ - CUR.error = TT_Err_Nested_DEFS; - return; + case 0x89: /* IDEF */ + case 0x2C: /* FDEF */ + CUR.error = TT_Err_Nested_DEFS; + return; - case 0x2D: /* ENDF */ - return; + case 0x2D: /* ENDF */ + return; } } } - /*************************************************************************/ /* */ /* ENDF[]: END Function definition */ @@ -4643,6 +4759,13 @@ CUR.step_ins = FALSE; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* CUR.enhanced may be turned off prior to function calls. This + * ensures it is turned back on. + */ + CUR.enhanced = ( CUR.subpixel_hinting || CUR.grayscale_hinting ); +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ + if ( pRec->Cur_Count > 0 ) { CUR.callTop++; @@ -4675,8 +4798,9 @@ FT_ULong F; TT_CallRec* pCrec; TT_DefRecord* def; - - +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Bool oldF; +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ /* first of all, check the index */ F = args[0]; @@ -4713,6 +4837,17 @@ if ( !def->active ) goto Fail; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* This is test code used to detect inline delta functions */ + oldF = def->inline; + if (CUR.enhanced) + { + if ( def->inline ) { CUR.infunc = TRUE; } + } + + /* if ( F == 35 || F == 34 ){ CUR.enhanced = 0; printf ("ENTERING %d ", F); }*/ +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ + /* check the call stack */ if ( CUR.callTop >= CUR.callSize ) { @@ -4733,6 +4868,12 @@ def->start ); CUR.step_ins = FALSE; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + { + CUR.infunc = oldF; /*printf ("Leaving %d ", F);*/ + } + /*if ( F == 35 || F == 34 ){ CUR.enhanced = 1; printf ("Leaving %d ", F); }*/ +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ return; Fail: @@ -4752,7 +4893,9 @@ FT_ULong F; TT_CallRec* pCrec; TT_DefRecord* def; - +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Bool oldF; +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ /* first of all, check the index */ F = args[1]; @@ -4788,7 +4931,16 @@ /* check that the function is active */ if ( !def->active ) goto Fail; - +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + oldF=def->inline; + if (CUR.enhanced) + { + if ( def->inline ) + { + CUR.infunc = TRUE; + } + } +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ /* check stack */ if ( CUR.callTop >= CUR.callSize ) { @@ -4811,6 +4963,11 @@ CUR.step_ins = FALSE; } +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + { + CUR.infunc = oldF; + } +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ return; Fail: @@ -5154,6 +5311,10 @@ } } +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* Disable Type 2 Vacuform Rounds - e.g. Arial Narrow */ + if ( CUR.enhanced && FT_ABS(D) == 64) D += 1; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ args[0] = D; } @@ -5634,13 +5795,21 @@ } #endif - if ( CUR.GS.freeVector.x != 0 ) +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( !CUR.enhanced + || ( CUR.enhanced + && ( CUR.sph_tweak_flags & SPH_TWEAK_ALLOW_MOVEZP2_FREEV ) )) { - CUR.zp2.cur[point].x += dx; - if ( touch ) - CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + if ( CUR.GS.freeVector.x != 0 ) + { + CUR.zp2.cur[point].x += dx; + if ( touch ) + CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; + } +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING } - +#endif if ( CUR.GS.freeVector.y != 0 ) { CUR.zp2.cur[point].y += dy; @@ -5649,7 +5818,6 @@ } } - /*************************************************************************/ /* */ /* SHP[a]: SHift Point by the last point */ @@ -5819,7 +5987,7 @@ { FT_F26Dot6 dx, dy; FT_UShort point; - + FT_Int B1, B2; if ( CUR.top < CUR.GS.loop + 1 ) { @@ -5863,11 +6031,64 @@ } } else - MOVE_Zp2_Point( point, dx, dy, TRUE ); + { +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* The conditionals here still to not do a perfect job and need work. + * + * If not using enhanced rendering, allow ZP2 move. + * + * If using enhanced rendering, allow ZP2 point move if: + * - The glyph is composite + * - The glyph is specifically set to allow SHPIX moves + * - The move is in the Y direction on a previously touched point + * + * It seems that what qualifies as a previously touched point varies + * somewhat from font to font. Some render better when either X or Y + * must be touched ( SPH_TWEAK_SHPIX_CLASS_A ) and some render better + * when both must be touched. + */ + + if ( CUR.enhanced ) + { + B1 = CUR.zp2.cur[point].y; + if ( CUR.is_composite + || ( CUR.sph_tweak_flags & SPH_TWEAK_DO_SHPIX ) + || ( /*CUR.infunc && !(CUR.sph_tweak_flags + & SPH_TWEAK_SKIP_NONPIXEL_INLINE_MOVES) &&*/ /*CUR.infunc &&*/ + CUR.GS.freeVector.y != 0 + && CUR.iup_called == 0 + && CUR.iupy_called == 0 + && ( + ( (CUR.sph_tweak_flags & SPH_TWEAK_SHPIX_CLASS_A ) + && (( CUR.pts.tags[point] & FT_CURVE_TAG_TOUCH_X ) != 0 + || ( CUR.pts.tags[point] & FT_CURVE_TAG_TOUCH_Y ) != 0 )) + || ( !(CUR.sph_tweak_flags & SPH_TWEAK_SHPIX_CLASS_A ) + && (( CUR.pts.tags[point] & FT_CURVE_TAG_TOUCH_X ) != 0 + && ( CUR.pts.tags[point] & FT_CURVE_TAG_TOUCH_Y ) != 0 )) + ) + /*|| !CUR.infunc*/ ) ) + MOVE_Zp2_Point( point, dx, dy, TRUE ); + + B2 = CUR.zp2.cur[point].y; + + /* Reverse moves that move the point off a pixel boundary */ + if ((CUR.sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_INLINE_MOVES) + && B1 % 64 == 0 + && B2 % 64 != 0 ) + { + /*printf ("Reversing ZP2 move ");*/ + MOVE_Zp2_Point( point, -dx, -dy, TRUE ); + } + } + else +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + MOVE_Zp2_Point( point, dx, dy, TRUE ); + + } CUR.GS.loop--; } - + /*printf("SHPIX:%d ", CUR.infunc);*/ CUR.GS.loop = 1; CUR.new_top = CUR.args; } @@ -5884,7 +6105,17 @@ { FT_UShort point; FT_F26Dot6 distance; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Int resolution = 1; + if ( CUR.enhanced ) + { + if ( CUR.GS.freeVector.x != 0 ) + resolution = Grids_Per_Pixel_X; + else if ( CUR.GS.freeVector.y != 0 ) + resolution = Grids_Per_Pixel_Y; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ point = (FT_UShort)args[0]; @@ -5908,6 +6139,15 @@ distance = CUR_Func_project( CUR.zp1.cur + point, CUR.zp0.cur + CUR.GS.rp0 ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* Subpixel Hinting - make MSIRP respect CVT cutin */ + /* Fixes "k" issue with Arial */ + if ( CUR.enhanced && CUR.GS.freeVector.x != 0 + && FT_ABS( distance - args[1]) + >= CUR.GS.control_value_cutin / resolution ) + distance = args[1]; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + CUR_Func_move( &CUR.zp1, point, args[1] - distance ); CUR.GS.rp1 = CUR.GS.rp0; @@ -5930,7 +6170,19 @@ FT_UShort point; FT_F26Dot6 cur_dist, distance; + FT_Int resolution = 1; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( CUR.enhanced ) + { + if ( CUR.GS.freeVector.x != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MDAP )) + resolution = Grids_Per_Pixel_X; + else if ( CUR.GS.freeVector.y != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MDAP )) + resolution = Grids_Per_Pixel_Y; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ point = (FT_UShort)args[0]; @@ -5946,8 +6198,8 @@ if ( ( CUR.opcode & 1 ) != 0 ) { cur_dist = CUR_fast_project( &CUR.zp0.cur[point] ); - distance = CUR_Func_round( cur_dist, - CUR.tt_metrics.compensations[0] ) - cur_dist; + distance = CUR_Func_round( cur_dist, + CUR.tt_metrics.compensations[0], resolution ) - cur_dist; } else distance = 0; @@ -5972,7 +6224,19 @@ FT_UShort point; FT_F26Dot6 distance, org_dist; + FT_Int resolution = 1; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( CUR.enhanced ) + { + if ( CUR.GS.freeVector.x != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MIAP )) + resolution = Grids_Per_Pixel_X; + else if ( CUR.GS.freeVector.y != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MIAP )) + resolution = Grids_Per_Pixel_Y; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ cvtEntry = (FT_ULong)args[1]; point = (FT_UShort)args[0]; @@ -6020,10 +6284,13 @@ if ( ( CUR.opcode & 1 ) != 0 ) /* rounding and control cutin flag */ { - if ( FT_ABS( distance - org_dist ) > CUR.GS.control_value_cutin ) - distance = org_dist; - - distance = CUR_Func_round( distance, CUR.tt_metrics.compensations[0] ); + { + if ( FT_ABS( distance - org_dist ) + > CUR.GS.control_value_cutin / resolution ) + distance = org_dist; + distance = CUR_Func_round( distance, + CUR.tt_metrics.compensations[0], resolution ); + } } CUR_Func_move( &CUR.zp0, point, distance - org_dist ); @@ -6044,6 +6311,24 @@ { FT_UShort point; FT_F26Dot6 org_dist, distance; + FT_Int minimum_distance_factor = 64; + FT_Int resolution = 1; + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( CUR.enhanced ) + { + if ( CUR.GS.freeVector.x != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MDRP )) + { + resolution = Grids_Per_Pixel_X; + minimum_distance_factor = 64 - resolution / 3; + } + else if ( CUR.GS.freeVector.y != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MDRP )) + resolution = Grids_Per_Pixel_Y; + + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ point = (FT_UShort)args[0]; @@ -6107,13 +6392,15 @@ /* round flag */ if ( ( CUR.opcode & 4 ) != 0 ) + { distance = CUR_Func_round( org_dist, - CUR.tt_metrics.compensations[CUR.opcode & 3] ); + CUR.tt_metrics.compensations[CUR.opcode & 3], resolution ); + } else distance = ROUND_None( org_dist, - CUR.tt_metrics.compensations[CUR.opcode & 3] ); + CUR.tt_metrics.compensations[CUR.opcode & 3], resolution ); /* minimum distance flag */ @@ -6121,13 +6408,17 @@ { if ( org_dist >= 0 ) { - if ( distance < CUR.GS.minimum_distance ) - distance = CUR.GS.minimum_distance; + if ( distance < FT_MulDiv(minimum_distance_factor, + CUR.GS.minimum_distance, 64) ) + distance = FT_MulDiv(minimum_distance_factor, + CUR.GS.minimum_distance, 64); } else { - if ( distance > -CUR.GS.minimum_distance ) - distance = -CUR.GS.minimum_distance; + if ( distance > -FT_MulDiv( minimum_distance_factor, + CUR.GS.minimum_distance, 64 ) ) + distance = -FT_MulDiv( minimum_distance_factor, + CUR.GS.minimum_distance, 64 ); } } @@ -6163,10 +6454,30 @@ cur_dist, org_dist; + FT_Int minimum_distance_factor = 64; + FT_Int B1, B2, resolution = 1; point = (FT_UShort)args[0]; cvtEntry = (FT_ULong)( args[1] + 1 ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( CUR.enhanced ) + { + if ( CUR.GS.freeVector.x != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MIRP )) + { + resolution = Grids_Per_Pixel_X; + /* High value emboldens glyphs at lower ppems ( < 14 ) */ + /* Courier looks better with 52 */ + /* MS Cleartype Rasterizer supposedly uses 32 */ + minimum_distance_factor = 64 - resolution / 3; + } + else if ( CUR.GS.freeVector.y != 0 + && !( CUR.sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND_MIRP )) + resolution = Grids_Per_Pixel_Y; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */ if ( BOUNDS( point, CUR.zp1.n_points ) || @@ -6230,36 +6541,55 @@ /* refer to the same zone. */ if ( CUR.GS.gep0 == CUR.GS.gep1 ) - if ( FT_ABS( cvt_dist - org_dist ) >= CUR.GS.control_value_cutin ) + if ( FT_ABS( cvt_dist - org_dist ) >= CUR.GS.control_value_cutin / resolution ) cvt_dist = org_dist; distance = CUR_Func_round( cvt_dist, - CUR.tt_metrics.compensations[CUR.opcode & 3] ); + CUR.tt_metrics.compensations[CUR.opcode & 3], resolution ); } else distance = ROUND_None( cvt_dist, - CUR.tt_metrics.compensations[CUR.opcode & 3] ); + CUR.tt_metrics.compensations[CUR.opcode & 3], resolution ); /* minimum distance test */ - if ( ( CUR.opcode & 8 ) != 0 ) { if ( org_dist >= 0 ) { - if ( distance < CUR.GS.minimum_distance ) - distance = CUR.GS.minimum_distance; + if ( distance < FT_MulDiv( minimum_distance_factor, + CUR.GS.minimum_distance, 64 ) ) + distance = FT_MulDiv( minimum_distance_factor, + CUR.GS.minimum_distance, 64 ); } else { - if ( distance > -CUR.GS.minimum_distance ) - distance = -CUR.GS.minimum_distance; + if ( distance > -FT_MulDiv( minimum_distance_factor, + CUR.GS.minimum_distance, 64 ) ) + distance = -FT_MulDiv( minimum_distance_factor, + CUR.GS.minimum_distance, 64 ); } } + B1 = CUR.zp1.cur[point].y; + CUR_Func_move( &CUR.zp1, point, distance - cur_dist ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( CUR.enhanced && CUR.sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_INLINE_MOVES) + { + B2 = CUR.zp1.cur[point].y; + + if ( ( CUR.GS.freeVector.x != 0 && B1 % 64 == 0 && B2 % 64 != 0 ) + || ( CUR.GS.freeVector.y != 0 && B2 % 64 != 0 ) ) + { + /* reverse the MIRP move. Ideally this could be implemented better */ + CUR_Func_move( &CUR.zp1, point, -(distance - cur_dist) ); + } + } +#endif /*TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ + CUR.GS.rp1 = CUR.GS.rp0; if ( ( CUR.opcode & 16 ) != 0 ) @@ -6751,6 +7081,14 @@ contour = 0; point = 0; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( CUR.enhanced ) + { + CUR.iup_called = 1; + if ( CUR.sph_tweak_flags & SPH_TWEAK_SKIP_IUP ) return; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + do { end_point = CUR.pts.contours[contour] - CUR.pts.first_point; @@ -6820,7 +7158,19 @@ FT_UShort A; FT_ULong C; FT_Long B; - +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + FT_Byte orig_round_state; + FT_Int resolution = 1; + FT_UShort B1, B2; + + if (CUR.enhanced ) + { + if ( CUR.GS.freeVector.x != 0 ) + resolution = Grids_Per_Pixel_X; + else if ( CUR.GS.freeVector.y != 0 ) + resolution = Grids_Per_Pixel_Y; + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING /* Delta hinting is covered by US Patent 5159668. */ @@ -6889,8 +7239,81 @@ if ( B >= 0 ) B++; B = B * 64 / ( 1L << CUR.GS.delta_shift ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS + /* Undocumented hack that will round the point prior to or instead + * of the delta move. Fixes glitches in various fonts due to bad + * y-hinting routines. + */ + if ( CUR.enhanced && CUR.GS.freeVector.y != 0 ) + { + orig_round_state= CUR.GS.round_state; + + if ( CUR.sph_tweak_flags & SPH_TWEAK_DELTAP_RDTG ) + { + COMPUTE_Round( TT_Round_Down_To_Grid ); + B = CUR_Func_round( B, CUR.tt_metrics.compensations[0], 1 ); + } + + else if ( CUR.sph_tweak_flags & SPH_TWEAK_DELTAP_RUTG ) + { + COMPUTE_Round( TT_Round_Up_To_Grid ); + B = CUR_Func_round( B, CUR.tt_metrics.compensations[0], 1 ); + } + + else if ( CUR.sph_tweak_flags & SPH_TWEAK_DELTAP_RTG ) + { + COMPUTE_Round( TT_Round_To_Grid ); + B = CUR_Func_round( B, CUR.tt_metrics.compensations[0], 1 ); + } + + COMPUTE_Round( orig_round_state ); + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS */ + + /* Allow delta move if: + * - Not using enhanced rendering + * - Glyph is specifically set to allow it + * - Glyph is composite + */ + if ( !CUR.enhanced + || CUR.sph_tweak_flags & SPH_TWEAK_ALWAYS_DO_DELTAP + || CUR.is_composite ) + CUR_Func_move( &CUR.zp0, A, B ); + else if ( !( CUR.sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) ) + { + /* Save the y value of the point now. Compare after move */ + B1 = CUR.zp0.cur[A].y; + + /* Allow delta move if using enhanced rendering, IUP has not + * been called, and point is touched on X or Y. + * + * Working code, but needs more features. + */ + if ( CUR.enhanced && CUR.GS.freeVector.y != 0 + && CUR.iup_called == 0 && CUR.iupy_called == 0 + && ( ( CUR.pts.tags[A] & FT_CURVE_TAG_TOUCH_X ) != 0 + || ( CUR.pts.tags[A] & FT_CURVE_TAG_TOUCH_Y ) != 0 ) ) + /* Should resolution always be 1 for this move ??? */ + CUR_Func_move( &CUR.zp0, A, B ); + + B2 = CUR.zp0.cur[A].y; + + /* Reverse this move if it results in a move off a pixel + * boundary. + */ + if ( ( CUR.sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_INLINE_MOVES ) + && B1 % 64 == 0 + && B2 % 64 != 0 ) + { + CUR_Func_move( &CUR.zp0, A, -B ); + } + + } +#else CUR_Func_move( &CUR.zp0, A, B ); +#endif /* *TT_CONFIG_OPTION_SUBPIXEL_HINTING */ } } else @@ -7015,22 +7438,100 @@ K = 0; - /* We return MS rasterizer version 1.7 for the font scaler. */ - if ( ( args[0] & 1 ) != 0 ) - K = 35; - - /* Has the glyph been rotated? */ + /********************************/ + /* RASTERIZER VERSION */ + /* Selector Bit: 0 */ + /* Return Bit(s): 0-7 */ + /* */ +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( ( args[0] & 1 ) != 0 + && CUR.enhanced + && !( CUR.sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) ){ + K = CUR.rasterizer_version; /*printf (" SETTING AS 37 \n" );*/ } + else +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + if ( ( args[0] & 1 ) != 0 ){ + K = 35; /*printf (" SETTING AS 35 \n" );*/ + } + /********************************/ + /* GLYPH ROTATED */ + /* Selector Bit: 1 */ + /* Return Bit(s): 8 */ + /* */ if ( ( args[0] & 2 ) != 0 && CUR.tt_metrics.rotated ) - K |= 0x80; + K |= 1 << 8; - /* Has the glyph been stretched? */ + /********************************/ + /* GLYPH STRETCHED */ + /* Selector Bit: 2 */ + /* Return Bit(s): 9 */ + /* */ if ( ( args[0] & 4 ) != 0 && CUR.tt_metrics.stretched ) - K |= 1 << 8; + /*K |= 1 << 8; Original value appears to be incorrect?? */ + K |= 1 << 9; - /* Are we hinting for grayscale? */ + /********************************/ + /* HINTING FOR GRAYSCALE */ + /* Selector Bit: 5 */ + /* Return Bit(s): 12 */ + /* */ if ( ( args[0] & 32 ) != 0 && CUR.grayscale ) K |= 1 << 12; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + if ( CUR.enhanced && !( CUR.sph_tweak_flags & SPH_TWEAK_RASTERIZER_35 ) ) + { + /********************************/ + /* HINTING FOR GRAYSCALE */ + /* Selector Bit: 5 */ + /* Return Bit(s): 12 */ + /* */ + if ( ( args[0] & 32 ) != 0 && CUR.grayscale_hinting ) + K |= 1 << 12; + + /********************************/ + /* HINTING FOR SUBPIXEL */ + /* Selector Bit: 6 */ + /* Return Bit(s): 13 */ + /* */ + if ( ( args[0] & 64 ) != 0 && CUR.subpixel_hinting ) + { + K |= 1 << 13; + + /* The below are irrelevant if subpixel_hinting is not set */ + + /********************************/ + /* COMPATIBLE WIDTHS ENABLED */ + /* Selector Bit: 7 */ + /* Return Bit(s): 14 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 128 ) != 0 && CUR.compatible_widths ) + { + K |= 1 << 14; + } + + /********************************/ + /* SYMMETRICAL SMOOTHING */ + /* Selector Bit: 8 */ + /* Return Bit(s): 15 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 256 ) != 0 && CUR.symmetrical_smoothing ) + K |= 1 << 15; + + /********************************/ + /* HINTING FOR BGR? */ + /* Selector Bit: 9 */ + /* Return Bit(s): 16 */ + /* */ + /* Functionality still needs to be added */ + if ( ( args[0] & 512 ) != 0 && CUR.bgr ) + K |= 1 << 16; + } + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + args[0] = K; } @@ -7405,6 +7906,14 @@ cur = *exc; #endif + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /* Ensure some variables are set for this run */ + CUR.iup_called = FALSE; + CUR.iupy_called = FALSE; + CUR.infunc = FALSE; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + /* set CVT functions */ CUR.tt_metrics.ratio = 0; if ( CUR.metrics.x_ppem != CUR.metrics.y_ppem ) @@ -7683,8 +8192,16 @@ case 0x30: /* IUP */ + Ins_IUP( EXEC_ARG_ args ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + CUR.iup_called = TRUE; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + break; case 0x31: /* IUP */ Ins_IUP( EXEC_ARG_ args ); +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + CUR.iupy_called = TRUE; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING*/ break; case 0x32: /* SHP */ diff -Nur freetype-2.4.3.orig/src/truetype/ttinterp.h freetype-2.4.3.new/src/truetype/ttinterp.h --- freetype-2.4.3.orig/src/truetype/ttinterp.h 2010-08-05 16:56:05.000000000 -0500 +++ freetype-2.4.3.new/src/truetype/ttinterp.h 2010-10-23 14:00:05.553373908 -0500 @@ -68,7 +68,8 @@ /* Rounding function */ typedef FT_F26Dot6 (*TT_Round_Func)( EXEC_OP_ FT_F26Dot6 distance, - FT_F26Dot6 compensation ); + FT_F26Dot6 compensation, + FT_Int resolution ); /* Point displacement along the freedom vector routine */ typedef void @@ -106,6 +107,24 @@ } TT_CallRec, *TT_CallStack; +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /*************************************************************************/ + /* */ + /* This structure defines a rule used to tweak subpixel hinting for */ + /* various fonts. "", 0, "", NULL value indicates to match any value. */ + /* */ + + typedef struct SPH_TweakRule_ + { + const char family[32]; + const int ppem; + const char style[32]; + const char glyph; + } SPH_TweakRule; + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + + /*************************************************************************/ /* */ /* The main structure for the interpreter which collects all necessary */ @@ -215,7 +234,32 @@ TT_Set_CVT_Func func_write_cvt; /* write a cvt entry (in pixels) */ TT_Set_CVT_Func func_move_cvt; /* incr a cvt entry (in pixels) */ - FT_Bool grayscale; /* are we hinting for grayscale? */ + FT_Bool grayscale; /* are we hinting for grayscale? */ + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + TT_Round_Func func_round_sphn; /* subpixel rounding fuction */ + + FT_Bool grayscale_hinting; /* are we hinting for subpixel? */ + FT_Bool subpixel_hinting; /* are we hinting for subpixel? */ + FT_Bool enhanced; /* are we using enhanced rendering? */ + /* ( grayscale_hinting || subpixel_hinting ) */ + FT_Bool native_hinting; /* do native hinting when true */ + /* the following 3 are unimplemented but here for future reference */ + + FT_Bool compatible_widths; /* are we using compatible widths?*/ + FT_Bool symmetrical_smoothing; /* symmetrical_smoothing? */ + FT_Bool bgr; /* are we using bgr, not rgb? */ + FT_Int rasterizer_version;/* return ms rasterizer version */ + + FT_Bool iup_called; /* IUP[x] been called for glyph? */ + FT_Bool iupy_called; /* IUP[y] been called for glyph? */ + FT_Bool infunc; /* inside an inline delta func? */ + + FT_ULong sph_tweak_flags; /* flags to control hint tweaks */ + + FT_Int num_delta_funcs; + FT_ULong inline_delta_funcs[5]; +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ } TT_ExecContextRec; diff -Nur freetype-2.4.3.orig/src/truetype/ttobjs.h freetype-2.4.3.new/src/truetype/ttobjs.h --- freetype-2.4.3.orig/src/truetype/ttobjs.h 2009-07-03 08:28:24.000000000 -0500 +++ freetype-2.4.3.new/src/truetype/ttobjs.h 2010-10-22 19:25:46.075900111 -0500 @@ -177,6 +177,7 @@ FT_Long start; /* where does it start? */ FT_UInt opc; /* function #, or instruction code */ FT_Bool active; /* is it active? */ + FT_Bool inline; /* function that defines inline delta */ } TT_DefRecord, *TT_DefArray; diff -Nur freetype-2.4.3.orig/src/truetype/ttsubpixel.c freetype-2.4.3.new/src/truetype/ttsubpixel.c --- freetype-2.4.3.orig/src/truetype/ttsubpixel.c 1969-12-31 18:00:00.000000000 -0600 +++ freetype-2.4.3.new/src/truetype/ttsubpixel.c 2010-11-14 09:25:59.206993605 -0600 @@ -0,0 +1,171 @@ +/***************************************************************************/ +/* */ +/* ttsubpixel.c */ +/* */ +/* TrueType Subpixel Hinting. */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, */ +/* 2010 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include +#include FT_INTERNAL_DEBUG_H +#include FT_INTERNAL_CALC_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_SFNT_H +#include FT_TRUETYPE_TAGS_H +#include FT_OUTLINE_H + +#include "ttsubpixel.h" + + + /*************************************************************************/ + /* */ + /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ + /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ + /* messages during execution. */ + /* */ +#undef FT_COMPONENT +#define FT_COMPONENT trace_ttgload + + + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS + static FT_Bool + sph_test_tweak ( TT_Face face, + FT_String* family, + int ppem, + FT_String* style, + FT_UInt glyph_index, + SPH_TweakRule* rule, + int num_rules ) + { + FT_UInt i; + + /* rule checks may be able to be optimized further */ + for ( i = 0; i < num_rules; i++ ) + { + if ( family && ( strcmp( rule[i].family, "" ) == 0 + || strcmp ( rule[i].family, family ) == 0 ) ) + if ( rule[i].ppem == 0 + || (rule[i].ppem == ppem) ) + if ( ( style && strcmp ( rule[i].style, "" ) == 0 ) + || strcmp ( rule[i].style, style ) == 0 ) + if ( rule[i].glyph == 0 + || FT_Get_Char_Index( (FT_Face)face, rule[i].glyph ) + == glyph_index ) + { + /* printf ("%s,%d,%s,%c ", family, ppem, style, rule[i].glyph); */ + return TRUE; + } + } + return FALSE; + } + + static void + sph_set_tweaks( TT_Loader loader, + FT_UInt glyph_index ) + { + TT_Face face = (TT_Face)loader->face; + FT_String* family = face->root.family_name; + int ppem = loader->size->metrics.x_ppem; + FT_String* style = face->root.style_name; + + /* loader->exec->sph_tweak_flags = 0x0000; */ + + /*printf ("%s,%d,%s,%c ", family, ppem, style, glyph_index); */ + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + NORMAL_ROUND_MIRP_Rules, NORMAL_ROUND_MIRP_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_NORMAL_ROUND_MIRP; + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + NORMAL_ROUND_MDRP_Rules, NORMAL_ROUND_MDRP_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_NORMAL_ROUND_MDRP; + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + NORMAL_ROUND_MDAP_Rules, NORMAL_ROUND_MDAP_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_NORMAL_ROUND_MDAP; + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + NORMAL_ROUND_MIAP_Rules, NORMAL_ROUND_MIAP_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_NORMAL_ROUND_MIAP; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + SKIP_IUP_Rules, SKIP_IUP_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_SKIP_IUP; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALWAYS_SKIP_DELTAP_Rules, ALWAYS_SKIP_DELTAP_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_ALWAYS_SKIP_DELTAP; + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALWAYS_DO_DELTAP_Rules, ALWAYS_DO_DELTAP_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_ALWAYS_DO_DELTAP; + else if ( sph_test_tweak( face, family, ppem, style, glyph_index, + DELTAP_RTG_Rules, DELTAP_RTG_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_DELTAP_RTG; + else if ( sph_test_tweak( face, family, ppem, style, glyph_index, + DELTAP_RUTG_Rules, DELTAP_RUTG_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_DELTAP_RUTG; + else if ( sph_test_tweak( face, family, ppem, style, glyph_index, + DELTAP_RDTG_Rules, DELTAP_RDTG_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_DELTAP_RDTG; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALLOW_DMOVEX_FREEV_Rules, ALLOW_DMOVEX_FREEV_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_ALLOW_DMOVEX_FREEV; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALLOW_DMOVE_FREEV_Rules, ALLOW_DMOVE_FREEV_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_ALLOW_DMOVE_FREEV; + + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALLOW_DMOVEX_FREEV_Rules_Exceptions, ALLOW_DMOVEX_FREEV_RULES_EXCEPTIONS_SIZE ) ) + loader->exec->sph_tweak_flags &= ~SPH_TWEAK_ALLOW_DMOVEX_FREEV; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALLOW_DMOVE_FREEV_Rules_Exceptions, ALLOW_DMOVE_FREEV_RULES_EXCEPTIONS_SIZE ) ) + loader->exec->sph_tweak_flags &= ~SPH_TWEAK_ALLOW_DMOVE_FREEV; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + RASTERIZER_35_Rules, RASTERIZER_35_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_RASTERIZER_35; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALLOW_MOVEZP2_FREEV_Rules, ALLOW_MOVEZP2_FREEV_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_ALLOW_MOVEZP2_FREEV; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + ALLOW_MOVEZP2_FREEV_Rules_Exceptions, ALLOW_MOVEZP2_FREEV_RULES_EXCEPTIONS_SIZE ) ) + loader->exec->sph_tweak_flags &= ~SPH_TWEAK_ALLOW_MOVEZP2_FREEV; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + DO_RS_Rules, DO_RS_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_DO_RS; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + DO_SHPIX_Rules, DO_SHPIX_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_DO_SHPIX; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + SKIP_NONPIXEL_INLINE_MOVES_Rules, SKIP_NONPIXEL_INLINE_MOVES_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_SKIP_NONPIXEL_INLINE_MOVES; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + SHPIX_CLASS_A_Rules, SHPIX_CLASS_A_RULES_SIZE ) ) + loader->exec->sph_tweak_flags |= SPH_TWEAK_SHPIX_CLASS_A; + + if ( sph_test_tweak( face, family, ppem, style, glyph_index, + SHPIX_CLASS_A_Rules_Exceptions, SHPIX_CLASS_A_RULES_EXCEPTIONS_SIZE ) ) + loader->exec->sph_tweak_flags &= ~SPH_TWEAK_SHPIX_CLASS_A; + + } +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS */ +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ diff -Nur freetype-2.4.3.orig/src/truetype/ttsubpixel.h freetype-2.4.3.new/src/truetype/ttsubpixel.h --- freetype-2.4.3.orig/src/truetype/ttsubpixel.h 1969-12-31 18:00:00.000000000 -0600 +++ freetype-2.4.3.new/src/truetype/ttsubpixel.h 2010-11-14 19:16:58.095874324 -0600 @@ -0,0 +1,819 @@ +/***************************************************************************/ +/* */ +/* ttsubpixel.h */ +/* */ +/* TrueType Subpixel Hinting. */ +/* */ +/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, */ +/* 2010 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef __TTSUBPIXEL_H__ +#define __TTSUBPIXEL_H__ + +#include +#include "ttobjs.h" + +#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING + /*************************************************************************/ + /* */ + /* Tweak flags that are set for each glyph */ + /* */ + /* */ +#define SPH_TWEAK_NORMAL_ROUND_MIRP 0x00001 +#define SPH_TWEAK_NORMAL_ROUND_MDRP 0x00002 +#define SPH_TWEAK_DELTAP_RDTG 0x00004 +#define SPH_TWEAK_DELTAP_RTG 0x00008 +#define SPH_TWEAK_DELTAP_RUTG 0x00010 +#define SPH_TWEAK_ALLOW_DMOVEX_FREEV 0x00020 +#define SPH_TWEAK_ALLOW_DMOVE_FREEV 0x00040 +#define SPH_TWEAK_ALLOW_MOVEZP2_FREEV 0x00080 +#define SPH_TWEAK_ALWAYS_SKIP_DELTAP 0x00100 +#define SPH_TWEAK_SKIP_IUP 0x00200 +#define SPH_TWEAK_NORMAL_ROUND_MIAP 0x00400 +#define SPH_TWEAK_NORMAL_ROUND_MDAP 0x00800 +#define SPH_TWEAK_DO_RS 0x01000 +#define SPH_TWEAK_DO_SHPIX 0x02000 +#define SPH_TWEAK_ALWAYS_DO_DELTAP 0x04000 +#define SPH_TWEAK_SKIP_NONPIXEL_INLINE_MOVES 0x08000 +#define SPH_TWEAK_SHPIX_CLASS_A 0x10000 +#define SPH_TWEAK_RASTERIZER_35 0x20000 + + + static FT_Bool + sph_test_tweak ( TT_Face face, + FT_String* family, + int ppem, + FT_String* style, + FT_UInt glyph_index, + SPH_TweakRule* rule, + int num_rules ); + static void + sph_set_tweaks( TT_Loader loader, + FT_UInt glyph_index ); + + + /*************************************************************************/ + /* */ + /* These are groups of rules that affect how the TT Interpreter does */ + /* hinting. */ + /* */ + /* "" string or 0 int/char indicates to apply to all. */ + /* "-" used as dummy placeholders, but any non-matching string works */ + /* */ + /* Remaining rules are tweaks for various fonts / glyphs */ + /* Some of this could arguably be implemented in fontconfig, however: */ + /* */ + /* - Fontconfig can't set things on a glyph-by-glyph basis. */ + /* - The tweaks that happen here are very low-level, from an average */ + /* user's point of view and are best implemented in the hinter */ + /* */ + /* Ideally, some of these should be generalized across affected fonts, */ + /* and enabled by default in the code. The rule structure is designed so */ + /* that entirely new rules can easily be added when a new compatibility */ + /* feature is discovered. */ + /* */ + + /*************************************************************************/ + /* */ + /* TT_CONFIG_OPTION_SUBPIXEL_HINTING Rules */ + /* */ + /* Simply, this attempts to duplicate the fuctionality described here */ + /* and nothing more: */ + /* */ + /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ + /* */ + /* This mode is enabled when */ + /* TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS */ + /* is undefined */ + /* */ + + +#ifndef TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS + +#if FALSE /* THIS RULESET NOT CURRENTLY BEING USED */ + +#define BITMAP_WIDTHS FALSE +#define SET_SUBPIXEL FALSE +#define SET_GRAYSCALE TRUE +#define SET_MONOCHROME FALSE +#define SET_COMPATIBLE_WIDTHS TRUE +#define SET_RASTERIZER_VERSION 35 +#define Grids_Per_Pixel_X 1 +#define Grids_Per_Pixel_Y 1 + + +/********** MOVE RULES *************/ + +/* Allow a Direct_Move_X along freedom vector when matched */ +#define ALLOW_DMOVEX_FREEV_RULES_SIZE 1 +SPH_TweakRule ALLOW_DMOVEX_FREEV_Rules +[ALLOW_DMOVEX_FREEV_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +/* Allow a Direct_Move along freedom vector when matched */ +#define ALLOW_DMOVE_FREEV_RULES_SIZE 1 +SPH_TweakRule ALLOW_DMOVE_FREEV_Rules +[ALLOW_DMOVE_FREEV_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +/* Allow a ZP2 Move along freedom vector when matched */ +#define ALLOW_MOVEZP2_FREEV_RULES_SIZE 1 +SPH_TweakRule ALLOW_MOVEZP2_FREEV_Rules +[ALLOW_MOVEZP2_FREEV_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +/* Don't skip RS calls */ +#define DO_RS_RULES_SIZE 1 +SPH_TweakRule DO_RS_Rules +[DO_RS_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Force requested SHPIX operations when matched */ +#define DO_SHPIX_RULES_SIZE 1 +SPH_TweakRule DO_SHPIX_Rules +[DO_SHPIX_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define SKIP_NONPIXEL_INLINE_MOVES_RULES_SIZE 1 +SPH_TweakRule SKIP_NONPIXEL_INLINE_MOVES_Rules +[SKIP_NONPIXEL_INLINE_MOVES_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MIRP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MIRP_Rules +[NORMAL_ROUND_MIRP_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MIAP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MIAP_Rules +[NORMAL_ROUND_MIAP_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MDRP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MDRP_Rules +[NORMAL_ROUND_MDRP_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MDAP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MDAP_Rules +[NORMAL_ROUND_MDAP_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +/* Indicates that SHPIX needs to match a touched point on x OR y */ +#define SHPIX_CLASS_A_RULES_SIZE 1 +SPH_TweakRule SHPIX_CLASS_A_Rules +[SHPIX_CLASS_A_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Mystery rules that make SHPIX work on certain fonts/glyphs. */ +/* Indicates that SHPIX needs to match a touched point on x AND y */ +/* This is dirty and needs to be generalized and incorporated. */ +#define SHPIX_CLASS_A_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule SHPIX_CLASS_A_Rules_Exceptions +[SHPIX_CLASS_A_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define ALLOW_DMOVEX_FREEV_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule ALLOW_DMOVEX_FREEV_Rules_Exceptions +[ALLOW_DMOVEX_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define ALLOW_DMOVE_FREEV_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule ALLOW_DMOVE_FREEV_Rules_Exceptions +[ALLOW_DMOVE_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define ALLOW_MOVEZP2_FREEV_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule ALLOW_MOVEZP2_FREEV_Rules_Exceptions +[ALLOW_MOVEZP2_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Skip IUP instructions when matched */ +#define SKIP_IUP_RULES_SIZE 1 +SPH_TweakRule SKIP_IUP_Rules +[SKIP_IUP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Skip DELTAP instructions when matched */ +#define ALWAYS_SKIP_DELTAP_RULES_SIZE 1 +SPH_TweakRule ALWAYS_SKIP_DELTAP_Rules +[ALWAYS_SKIP_DELTAP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Always do DELTAP instructions when matched */ +#define ALWAYS_DO_DELTAP_RULES_SIZE 1 +SPH_TweakRule ALWAYS_DO_DELTAP_Rules +[ALWAYS_DO_DELTAP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Do an extra RTG instruction in DELTAP when matched */ +#define DELTAP_RTG_RULES_SIZE 1 +SPH_TweakRule DELTAP_RTG_Rules +[DELTAP_RTG_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Do an extra RUTG instruction in DELTAP when matched */ +#define DELTAP_RUTG_RULES_SIZE 1 +SPH_TweakRule DELTAP_RUTG_Rules +[DELTAP_RUTG_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Do an extra RDTG instruction in DELTAP when matched */ +#define DELTAP_RDTG_RULES_SIZE 1 +SPH_TweakRule DELTAP_RDTG_Rules +[DELTAP_RDTG_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Return MS rasterizer 35 when matched */ +#define RASTERIZER_35_RULES_SIZE 1 +SPH_TweakRule RASTERIZER_35_Rules +[RASTERIZER_35_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; +#endif + + +#define BITMAP_WIDTHS FALSE +#define SET_SUBPIXEL TRUE +#define SET_GRAYSCALE FALSE +#define SET_MONOCHROME FALSE +#define SET_COMPATIBLE_WIDTHS FALSE +#define SET_RASTERIZER_VERSION 37 +#define Grids_Per_Pixel_X 64 +#define Grids_Per_Pixel_Y 1 + + +/********** MOVE RULES *************/ + +/* Allow a Direct_Move_X along freedom vector when matched */ +#define ALLOW_DMOVEX_FREEV_RULES_SIZE 1 +SPH_TweakRule ALLOW_DMOVEX_FREEV_Rules +[ALLOW_DMOVEX_FREEV_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Allow a Direct_Move along freedom vector when matched */ +#define ALLOW_DMOVE_FREEV_RULES_SIZE 1 +SPH_TweakRule ALLOW_DMOVE_FREEV_Rules +[ALLOW_DMOVE_FREEV_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Allow a ZP2 Move along freedom vector when matched */ +#define ALLOW_MOVEZP2_FREEV_RULES_SIZE 1 +SPH_TweakRule ALLOW_MOVEZP2_FREEV_Rules +[ALLOW_MOVEZP2_FREEV_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Don't skip RS calls */ +#define DO_RS_RULES_SIZE 1 +SPH_TweakRule DO_RS_Rules +[DO_RS_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Force requested SHPIX operations when matched */ +#define DO_SHPIX_RULES_SIZE 1 +SPH_TweakRule DO_SHPIX_Rules +[DO_SHPIX_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define SKIP_NONPIXEL_INLINE_MOVES_RULES_SIZE 1 +SPH_TweakRule SKIP_NONPIXEL_INLINE_MOVES_Rules +[SKIP_NONPIXEL_INLINE_MOVES_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MIRP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MIRP_Rules +[NORMAL_ROUND_MIRP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MIAP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MIAP_Rules +[NORMAL_ROUND_MIAP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MDRP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MDRP_Rules +[NORMAL_ROUND_MDRP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define NORMAL_ROUND_MDAP_RULES_SIZE 1 +SPH_TweakRule NORMAL_ROUND_MDAP_Rules +[NORMAL_ROUND_MDAP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Indicates that SHPIX needs to match a touched point on x OR y */ +#define SHPIX_CLASS_A_RULES_SIZE 1 +SPH_TweakRule SHPIX_CLASS_A_Rules +[SHPIX_CLASS_A_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Mystery rules that make SHPIX work on certain fonts/glyphs. */ +/* Indicates that SHPIX needs to match a touched point on x AND y */ +/* This is dirty and needs to be generalized and incorporated. */ +#define SHPIX_CLASS_A_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule SHPIX_CLASS_A_Rules_Exceptions +[SHPIX_CLASS_A_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define ALLOW_DMOVEX_FREEV_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule ALLOW_DMOVEX_FREEV_Rules_Exceptions +[ALLOW_DMOVEX_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define ALLOW_DMOVE_FREEV_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule ALLOW_DMOVE_FREEV_Rules_Exceptions +[ALLOW_DMOVE_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +#define ALLOW_MOVEZP2_FREEV_RULES_EXCEPTIONS_SIZE 1 +SPH_TweakRule ALLOW_MOVEZP2_FREEV_Rules_Exceptions +[ALLOW_MOVEZP2_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Skip IUP instructions when matched */ +#define SKIP_IUP_RULES_SIZE 1 +SPH_TweakRule SKIP_IUP_Rules +[SKIP_IUP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Skip DELTAP instructions when matched */ +#define ALWAYS_SKIP_DELTAP_RULES_SIZE 1 +SPH_TweakRule ALWAYS_SKIP_DELTAP_Rules +[ALWAYS_SKIP_DELTAP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Always do DELTAP instructions when matched */ +#define ALWAYS_DO_DELTAP_RULES_SIZE 1 +SPH_TweakRule ALWAYS_DO_DELTAP_Rules +[ALWAYS_DO_DELTAP_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Do an extra RTG instruction in DELTAP when matched */ +#define DELTAP_RTG_RULES_SIZE 1 +SPH_TweakRule DELTAP_RTG_Rules +[DELTAP_RTG_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Do an extra RUTG instruction in DELTAP when matched */ +#define DELTAP_RUTG_RULES_SIZE 1 +SPH_TweakRule DELTAP_RUTG_Rules +[DELTAP_RUTG_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Do an extra RDTG instruction in DELTAP when matched */ +#define DELTAP_RDTG_RULES_SIZE 1 +SPH_TweakRule DELTAP_RDTG_Rules +[DELTAP_RDTG_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + +/* Return MS rasterizer 35 when matched */ +#define RASTERIZER_35_RULES_SIZE 1 +SPH_TweakRule RASTERIZER_35_Rules +[RASTERIZER_35_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + + + /*************************************************************************/ + /* */ + /* TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS Rules */ + /* */ + /* This set of rules is an attempt at enhancing the basic subpixel rules */ + /* defined above, to fix visual problems with individual fonts and */ + /* glyphs. */ + /* */ + /* This mode is enabled when */ + /* TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS */ + /* is defined */ + /* */ + /* ****************** WORK IN PROGRESS ******************* */ + /* */ + +#else + +#define BITMAP_WIDTHS FALSE +#define SET_SUBPIXEL TRUE +#define SET_GRAYSCALE FALSE +#define SET_MONOCHROME FALSE +#define SET_COMPATIBLE_WIDTHS FALSE +#define SET_RASTERIZER_VERSION 37 +#define Grids_Per_Pixel_X 64 +#define Grids_Per_Pixel_Y 1 + + +/* Don't avoid RS Rules (as the basic subpixel hinting does) */ +#define DO_RS_RULES_SIZE 1 +SPH_TweakRule DO_RS_Rules[DO_RS_RULES_SIZE] = { + { "-", 0, "", 0 }, +}; + + +/******************* DELTA RULES *********************/ + +/* Do requested SHPIX operations when matched. This requires ZP2 moves */ +/* to be enabled in order to get SHPIX moves in the X direction */ +/* Do all "Optimized for Cleartype" fonts need to be here??. */ +/* the below doesn't work because the bit is not set in MS cleartype fonts */ +/* CUR.face->header.Flags & 0x10000 */ + +#define DO_SHPIX_RULES_SIZE 7 +SPH_TweakRule DO_SHPIX_Rules[DO_SHPIX_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "-Verdana", 0, "Regular", 0 }, + { "-Verdana", 12, "Regular", 0 }, + { "Verdana", 13, "Regular", 0 }, + /* aligns to pixels nicely, but messes up some glyphs */ + { "-Times New Roman", 0, "Regular", 0 }, + { "+++Segoe UI", 0, "Regular", 0 }, + { "-Segoe UI", 0, "Semibold", 0 }, +}; + +/* Indicates that SHPIX needs to match a touched point on x OR y */ +#define SHPIX_CLASS_A_RULES_SIZE 1 +SPH_TweakRule SHPIX_CLASS_A_Rules[SHPIX_CLASS_A_RULES_SIZE] = { + { "", 0, "", 0 }, +}; + +/* Mystery rules that make SHPIX work on certain fonts/glyphs. */ +/* Indicates that SHPIX needs to match a touched point on x AND y */ +/* This is dirty and needs to be generalized and incorporated. */ +#define SHPIX_CLASS_A_RULES_EXCEPTIONS_SIZE 2 +SPH_TweakRule SHPIX_CLASS_A_Rules_Exceptions +[SHPIX_CLASS_A_RULES_EXCEPTIONS_SIZE] = { + { "---", 0, "", 0 }, + { "Arial", 11, "Regular", 's' }, +}; + +/* Skip moves that don't align to a pixel in various functions. */ +/* Fixes Tahoma, trebuchet oddities and some issues with $ */ +#define SKIP_NONPIXEL_INLINE_MOVES_RULES_SIZE 4 +SPH_TweakRule SKIP_NONPIXEL_INLINE_MOVES_Rules +[SKIP_NONPIXEL_INLINE_MOVES_RULES_SIZE] = { + { "", 0, "Regular", 0 }, + /* keeps the weight in the center of the N */ + { "", 0, "Regular", 'N' }, + { "Tahoma", 0, "Regular", 0 }, + { "==Trebuchet MS", 0, "Regular", 0 }, +}; + + +/********** MOVE RULES *************/ + +/* Allow a Direct_Move_X along X freedom vector when matched */ +#define ALLOW_DMOVEX_FREEV_RULES_SIZE 20 +SPH_TweakRule ALLOW_DMOVEX_FREEV_Rules[ALLOW_DMOVEX_FREEV_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "-", 0, "Regular", 0 }, + { "-", 0, "Italic", 0 }, + { "-", 0, "Regular", 0 }, + { "-Verdana", 12, "Regular", 0 }, + { "-Geneva", 0, "", 0 }, + { "-Courier New", 0, "Regular", 0 }, + { "-Courier New", 0, "", 0 }, + { "-Arial", 0, "Bold", 0 }, + { "Verdana", 13, "Regular", 0 }, + { "-Times New Roman", 0, "Regular", 0 }, + { "Arial", 13, "Regular", 0 }, + { "Arial", 14, "Regular", 0 }, + { "-Tahoma", 0, "Regular", 0 }, + { "+++Trebuchet MS", 0, "Regular", 0 }, + { "-Trebuchet MS", 0, "Bold", 0 }, + { "-Segoe UI", 0, "Semibold", 0 }, + { "-Segoe UI", 12, "Regular", 'H' }, + { "Arial Narrow", 0, "Regular", 0 }, + { "+++Andale Mono", 17, "Regular", 0 }, +}; + +/* Allow a Direct_Move along X freedom vector when matched */ +#define ALLOW_DMOVE_FREEV_RULES_SIZE 21 +SPH_TweakRule ALLOW_DMOVE_FREEV_Rules +[ALLOW_DMOVE_FREEV_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "-", 0, "Regular", 0 }, + { "-", 0, "Italic", 0 }, + { "-Verdana", 12, "Regular", 0 }, + { "Verdana", 13, "Regular", 0 }, + { "-Courier New", 0, "Bold", 0 }, + { "-Times New Roman", 0, "Regular", 0 }, + { "Arial", 13, "Regular", 0 }, + { "Arial", 14, "Regular", 0 }, + { "-Arial", 0, "Bold", 0 }, + { "-Tahoma", 0, "Regular", 0 }, + { "+++Trebuchet MS", 0, "Regular", 0 }, + { "-Trebuchet MS", 0, "Bold", 0 }, + { "-Geneva", 0, "", 0 }, + { "-------", 0, "Regular", 0 }, + { "-Segoe UI", 0, "Semibold", 0 }, + { "+++Segoe UI", 12, "Regular", 'H' }, + { "-----", 0, "Regular", 0 }, + { "Arial Narrow", 0, "Regular", 0 }, + { "+++Andale Mono", 17, "Regular", 0 }, + { "-Courier New", 0, "", 0 }, +}; + + +#define ALLOW_DMOVEX_FREEV_RULES_EXCEPTIONS_SIZE 9 +SPH_TweakRule ALLOW_DMOVEX_FREEV_Rules_Exceptions + [ALLOW_DMOVEX_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, + { "-Times New Roman", 0, "Regular", 'a' }, + { "-Times New Roman", 0, "Regular", 'q' }, + { "-Times New Roman", 0, "Regular", 'P' }, + { "-Times New Roman", 0, "Regular", 'R' }, + { "-Times New Roman", 0, "Regular", 'B' }, + { "Arial", 0, "Regular", '4' }, + { "Arial", 0, "Regular", 's' }, + { "Arial", 0, "Regular", '^' }, +}; + +#define ALLOW_DMOVE_FREEV_RULES_EXCEPTIONS_SIZE 9 +SPH_TweakRule ALLOW_DMOVE_FREEV_Rules_Exceptions +[ALLOW_DMOVE_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, + { "-Times New Roman", 0, "Regular", 'a' }, + { "-Times New Roman", 0, "Regular", 'q' }, + { "-Times New Roman", 0, "Regular", 'P' }, + { "-Times New Roman", 0, "Regular", 'R' }, + { "-Times New Roman", 0, "Regular", 'B' }, + { "Arial", 0, "Regular", '4' }, + { "Arial", 0, "Regular", 's' }, + { "Arial", 0, "Regular", '^' }, +}; + + +/* Allow a ZP2 Move along freedom vector when matched */ +/* This is called from SHP, SHPIX, SHC, SHZ */ +#define ALLOW_MOVEZP2_FREEV_RULES_SIZE 14 +SPH_TweakRule ALLOW_MOVEZP2_FREEV_Rules[ALLOW_MOVEZP2_FREEV_RULES_SIZE] = { + { "-", 0, "Regular", 0 }, + { "-Verdana", 12, "Regular", 0 }, + { "Verdana", 13, "Regular", 0 }, + { "-Times New Roman", 0, "Regular", 0 }, + { "-Courier New", 0, "Bold", 0 }, + { "-Tahoma", 0, "Regular", 0 }, + { "-Courier New", 0, "", 0 }, + { "Arial", 13, "Regular", 0 }, + { "Arial", 14, "Regular", 0 }, + { "-Arial", 0, "Bold", 0 }, + { "+++Trebuchet MS", 0, "Regular", 0 }, + { "-Trebuchet MS", 0, "Bold", 0 }, + { "-Verdana", 13, "Regular", 0 }, + /* this needs a bit of work though */ + { "-Microsoft Sans Serif", 0, "Regular", 0 }, +}; + +/* Return MS rasterizer 35 when matched */ +#define RASTERIZER_35_RULES_SIZE 1 +SPH_TweakRule RASTERIZER_35_Rules +[RASTERIZER_35_RULES_SIZE] = { + { "Times New Roman", 0, "Regular", 'i' }, +}; + +/************** DIRTY, DIRTY HACKS!!!!!!!! ***************/ + +#define ALLOW_MOVEZP2_FREEV_RULES_EXCEPTIONS_SIZE 11 +SPH_TweakRule ALLOW_MOVEZP2_FREEV_Rules_Exceptions +[ALLOW_MOVEZP2_FREEV_RULES_EXCEPTIONS_SIZE] = { + { "-", 0, "", 0 }, + { "Times New Roman", 0, "Regular", 'a' }, + { "Times New Roman", 0, "Regular", 'q' }, + { "Verdana", 13, "Regular", 'N' }, + { "Verdana", 13, "Regular", 'f' }, + { "Verdana", 13, "Regular", 'v' }, + { "-------", 13, "Regular", 'k' }, + { "Verdana", 13, "Regular", 'w' }, + { "Verdana", 13, "Regular", 'x' }, + { "Verdana", 13, "Regular", 'y' }, + { "Verdana", 13, "Regular", 'z' }, +}; + +/*********** ROUNDING ***************/ +/* These only have an effect on fonts that are allowed to move X (above) */ +/* It appears all MS cleartype fonts may be OK using normal rounds */ +#define NORMAL_ROUND_MIRP_RULES_SIZE 16 +SPH_TweakRule NORMAL_ROUND_MIRP_Rules[NORMAL_ROUND_MIRP_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "-Tahoma", 9, "Regular", 0 }, + { "-Courier New", 0, "Regular", 'W' }, + { "-Courier New", 0, "Regular", 'K' }, + { "-Courier New", 0, "Regular", 'k' }, + { "-Courier New", 0, "Regular", 'V' }, + { "-Courier New", 0, "Regular", 'O' }, + { "-Courier New", 0, "Regular", 'X' }, + { "-Courier New", 0, "Regular", 'Y' }, + { "-Courier New", 0, "Regular", 'A' }, + { "-Courier New", 0, "Regular", 'v' }, + { "-Courier New", 0, "Regular", 'z' }, + { "-Courier New", 0, "Regular", 'x' }, + { "-Courier New", 0, "Regular", 'y' }, + { "Calibri", 0, "Italic", 0 }, + { "Calibri", 0, "Bold Italic", 0 }, +}; + +#define NORMAL_ROUND_MIAP_RULES_SIZE 16 +SPH_TweakRule NORMAL_ROUND_MIAP_Rules[NORMAL_ROUND_MIAP_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "-Tahoma", 9, "Regular", 0 }, + { "-Courier New", 0, "Regular", 'W' }, + { "-Courier New", 0, "Regular", 'K' }, + { "-Courier New", 0, "Regular", 'k' }, + { "-Courier New", 0, "Regular", 'V' }, + { "-Courier New", 0, "Regular", 'O' }, + { "-Courier New", 0, "Regular", 'X' }, + { "-Courier New", 0, "Regular", 'Y' }, + { "-Courier New", 0, "Regular", 'A' }, + { "-Courier New", 0, "Regular", 'v' }, + { "-Courier New", 0, "Regular", 'z' }, + { "-Courier New", 0, "Regular", 'x' }, + { "-Courier New", 0, "Regular", 'y' }, + { "Calibri", 0, "Italic", 0 }, + { "Calibri", 0, "Bold Italic", 0 }, +}; + +#define NORMAL_ROUND_MDRP_RULES_SIZE 16 +SPH_TweakRule NORMAL_ROUND_MDRP_Rules[NORMAL_ROUND_MDRP_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "-Tahoma", 9, "Regular", 0 }, + { "-Courier New", 0, "Regular", 'W' }, + { "-Courier New", 0, "Regular", 'K' }, + { "-Courier New", 0, "Regular", 'k' }, + { "-Courier New", 0, "Regular", 'V' }, + { "-Courier New", 0, "Regular", 'O' }, + { "-Courier New", 0, "Regular", 'X' }, + { "-Courier New", 0, "Regular", 'Y' }, + { "-Courier New", 0, "Regular", 'A' }, + { "-Courier New", 0, "Regular", 'v' }, + { "-Courier New", 0, "Regular", 'z' }, + { "-Courier New", 0, "Regular", 'x' }, + { "-Courier New", 0, "Regular", 'y' }, + { "Calibri", 0, "Italic", 0 }, + { "Calibri", 0, "Bold Italic", 0 }, +}; + +#define NORMAL_ROUND_MDAP_RULES_SIZE 16 +SPH_TweakRule NORMAL_ROUND_MDAP_Rules[NORMAL_ROUND_MDAP_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "-Tahoma", 9, "Regular", 0 }, + { "-Courier New", 0, "Regular", 'W' }, + { "-Courier New", 0, "Regular", 'K' }, + { "-Courier New", 0, "Regular", 'k' }, + { "-Courier New", 0, "Regular", 'V' }, + { "-Courier New", 0, "Regular", 'O' }, + { "-Courier New", 0, "Regular", 'X' }, + { "-Courier New", 0, "Regular", 'Y' }, + { "-Courier New", 0, "Regular", 'A' }, + { "-Courier New", 0, "Regular", 'v' }, + { "-Courier New", 0, "Regular", 'z' }, + { "-Courier New", 0, "Regular", 'x' }, + { "-Courier New", 0, "Regular", 'y' }, + { "Calibri", 0, "Italic", 0 }, + { "Calibri", 0, "Bold Italic", 0 }, +}; + + +/* Skip IUP instructions when matched */ +#define SKIP_IUP_RULES_SIZE 6 +SPH_TweakRule SKIP_IUP_Rules[SKIP_IUP_RULES_SIZE] = { + { "Arial", 13, "Regular", 'a' }, + { "-", 0, "Regular", '2' }, + { "-", 0, "", 0 }, + { "-", 0, "Regular", 'a' }, + { "-", 0, "Regular", 'V' }, + { "-", 0, "Light", 0 }, +}; + +/* Skip DELTAP instructions when matched */ +#define ALWAYS_SKIP_DELTAP_RULES_SIZE 19 +SPH_TweakRule ALWAYS_SKIP_DELTAP_Rules[ALWAYS_SKIP_DELTAP_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "--Courier New", 0, "Regular", 'V' }, + { "Verdana", 10, "Regular", 0 }, + { "-Trebuchet MS", 0, "Regular", 'W' }, + { "-Trebuchet MS", 0, "Regular", 'w' }, + { "-Verdana", 0, "Italic", 'v' }, + { "-Verdana", 0, "Italic", 'w' }, + { "-Verdana", 0, "Italic", 'x' }, + { "-Verdana", 0, "Italic", 'y' }, + { "-Verdana", 0, "Italic", 'z' }, + { "-Verdana", 0, "Regular", 'v' }, + { "-Verdana", 10, "Regular", 'w' }, + { "-Verdana", 0, "Regular", 'y' }, + { "-Verdana", 0, "Regular", 'z' }, + { "-Arial Bold", 0, "Bold", 's' }, + { "Trebuchet MS", 14, "Regular", 'e' }, + { "Trebuchet MS", 0, "Italic", 0 }, + { "-Arial", 0, "Italic", 0 }, + { "-", 0, "Italic", 0 }, +}; + +/* Always do DELTAP instructions when matched */ +#define ALWAYS_DO_DELTAP_RULES_SIZE 3 +SPH_TweakRule ALWAYS_DO_DELTAP_Rules[ALWAYS_DO_DELTAP_RULES_SIZE] = { + { "-", 0, "", 0 }, + { "DejaVu Sans", 14, "Regular", 'k' }, + { "DejaVu Sans", 14, "Regular", 'K' }, +}; + +/* Do an extra RTG instruction in DELTAP when matched */ +#define DELTAP_RTG_RULES_SIZE 4 +SPH_TweakRule DELTAP_RTG_Rules[DELTAP_RTG_RULES_SIZE] = { + { "-Arial Unicode MS", 0, "Regular", 0 }, + { "-Microsoft Sans Serif", 0, "Regular", '0' }, + { "--Verdana", 0, "", 0 }, + { "-Trebuchet MS", 14, "Regular", 'e' }, +}; + +/* Do an extra RUTG instruction in DELTAP when matched */ +#define DELTAP_RUTG_RULES_SIZE 2 +SPH_TweakRule DELTAP_RUTG_Rules[DELTAP_RUTG_RULES_SIZE] = { + { "-", 14, "Regular", 'e' }, + { "-", 0, "", 0 }, +}; + +/* Do an extra RDTG instruction in DELTAP when matched */ +#define DELTAP_RDTG_RULES_SIZE 28 +SPH_TweakRule DELTAP_RDTG_Rules[DELTAP_RDTG_RULES_SIZE] = { + { "Calibri", 0, "Italic", 0 }, + { "Comic Sans MS", 0, "Regular", 0 }, + { "Lucida Grande", 0, "Regular", 'e' }, + { "Lucida Grande", 12, "Bold", 0 }, + { "Microsoft Sans Serif", 0, "Regular", '7' }, + { "Microsoft Sans Serif", 0, "Regular", 'O' }, + { "Microsoft Sans Serif", 0, "Regular", 'Q' }, + { "Microsoft Sans Serif", 0, "Regular", 'X' }, + { "Microsoft Sans Serif", 0, "Regular", 'e' }, + { "Microsoft Sans Serif", 0, "Regular", 'o' }, + { "-", 0, "", 0 }, + { "-", 0, "Regular", 'O' }, + { "-", 0, "Regular", 'U' }, + { "-", 0, "Regular", 'e' }, + { "-", 0, "Regular", 'g' }, + { "Tahoma", 0, "Bold", '0' }, + { "Tahoma", 16, "Bold", 'C' }, + { "Tahoma", 16, "Bold Italic", 'C' }, + { "Trebuchet MS", 0, "", '0' }, + { "-", 9, "", 'w' }, + { "Verdana", 0, "", '0' }, + { "Verdana", 0, "Bold Italic", '7' }, + { "Verdana", 0, "Bold Italic", 'v' }, + { "Verdana", 0, "Bold Italic", 'w' }, + { "Verdana", 0, "Bold", 0 }, + { "Verdana", 0, "Italic", 'o' }, + { "Verdana", 0, "Regular", 'x' }, + { "Trebuchet MS", 14, "Regular", 'e' }, +}; + +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING_ADDITIONAL_TWEAKS */ +#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ + +#endif /* __TTSUBPIXEL_H__ */