From bb4edc923597aa48da0d0ac217e7a80d41e77f0d Mon Sep 17 00:00:00 2001 From: David Turner Date: Sat, 21 Jan 2006 14:31:45 +0000 Subject: [PATCH] * src/autofit/aflatin.c, src/autofit/afwarp.h, src/autofit/afwarp.c, src/autofit/aftypes.h, src/autofit/afloader.c, src/autofit/autofit.c: adding experimental implementation of "warp hinting" (new hinting algorithm for gray-level and LCD rendering). It is disabled by default, you need to #define AF_USE_WARPER in aftypes.h to enable it. --- ChangeLog | 9 ++ src/autofit/Jamfile | 2 +- src/autofit/afhints.c | 23 ++++ src/autofit/afhints.h | 8 ++ src/autofit/aflatin.c | 24 ++++ src/autofit/afloader.c | 3 +- src/autofit/aftypes.h | 1 + src/autofit/afwarp.c | 270 +++++++++++++++++++++++++++++++++++++++++ src/autofit/afwarp.h | 40 ++++++ src/autofit/autofit.c | 3 + 10 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 src/autofit/afwarp.c create mode 100644 src/autofit/afwarp.h diff --git a/ChangeLog b/ChangeLog index e77a3f443..2e98a418a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2006-01-20 David Turner + + * src/autofit/aflatin.c, src/autofit/afwarp.h, src/autofit/afwarp.c, + src/autofit/aftypes.h, src/autofit/afloader.c, src/autofit/autofit.c: + + adding experimental implementation of "warp hinting" (new hinting + algorithm for gray-level and LCD rendering). It is disabled by default, + you need to #define AF_USE_WARPER in aftypes.h to enable it. + 2006-01-19 David Turner * src/sfnt/ttsbit0.c (tt_face_load_strike_metrics): fixed small diff --git a/src/autofit/Jamfile b/src/autofit/Jamfile index c353781a7..bf10323e2 100644 --- a/src/autofit/Jamfile +++ b/src/autofit/Jamfile @@ -16,7 +16,7 @@ SubDir FT2_TOP src autofit ; if $(FT2_MULTI) { - _sources = afangles afglobal afhints aflatin afloader afmodule afdummy ; + _sources = afangles afglobal afhints aflatin afloader afmodule afdummy afwarp ; } else { diff --git a/src/autofit/afhints.c b/src/autofit/afhints.c index b37ef0e26..f38a33ec4 100644 --- a/src/autofit/afhints.c +++ b/src/autofit/afhints.c @@ -1140,5 +1140,28 @@ } } +#ifdef AF_USE_WARPER + FT_LOCAL_DEF( void ) + af_glyph_hints_scale_dim( AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed scale, + FT_Pos delta ) + { + AF_Point points = hints->points; + AF_Point points_limit = points + hints->num_points; + AF_Point point; + + if ( dim == AF_DIMENSION_HORZ ) + { + for ( point = points; point < points_limit; point++ ) + point->x = FT_MulFix( point->fx, scale ) + delta; + } + else + { + for ( point = points; point < points_limit; point++ ) + point->y = FT_MulFix( point->fy, scale ) + delta; + } + } +#endif /* AF_USE_WARPER */ /* END */ diff --git a/src/autofit/afhints.h b/src/autofit/afhints.h index 6f1a8a59a..a5a28ac2f 100644 --- a/src/autofit/afhints.h +++ b/src/autofit/afhints.h @@ -272,6 +272,14 @@ FT_BEGIN_HEADER af_glyph_hints_align_weak_points( AF_GlyphHints hints, AF_Dimension dim ); +#ifdef AF_USE_WARPER + FT_LOCAL( void ) + af_glyph_hints_scale_dim( AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed scale, + FT_Pos delta ); +#endif + FT_LOCAL( void ) af_glyph_hints_done( AF_GlyphHints hints ); diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c index 0f17cc220..7a1b60bc7 100644 --- a/src/autofit/aflatin.c +++ b/src/autofit/aflatin.c @@ -20,6 +20,10 @@ #include "aferrors.h" +#ifdef AF_USE_WARPER +#include "afwarp.h" +#endif + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -1311,6 +1315,13 @@ /* compute flags depending on render mode, etc. */ mode = metrics->root.scaler.render_mode; +#ifdef AF_USE_WARPER + if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V ) + { + metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL; + } +#endif + scaler_flags = hints->scaler_flags; other_flags = 0; @@ -1940,6 +1951,19 @@ if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) || ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) ) { +#ifdef AF_USE_WARPER + if ( dim == AF_DIMENSION_HORZ && + metrics->root.scaler.render_mode == FT_RENDER_MODE_NORMAL ) + { + AF_WarperRec warper; + FT_Fixed scale; + FT_Pos delta; + + af_warper_compute( &warper, hints, dim, &scale, &delta ); + af_glyph_hints_scale_dim( hints, dim, scale, delta ); + continue; + } +#endif af_latin_hint_edges( hints, (AF_Dimension)dim ); af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim ); af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim ); diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c index 520a9ce8a..f0e3cae5f 100644 --- a/src/autofit/afloader.c +++ b/src/autofit/afloader.c @@ -191,7 +191,7 @@ AF_Edge edge2 = edge1 + axis->num_edges - 1; /* rightmost edge */ - +#ifndef AF_USE_WARPER if ( axis->num_edges > 1 ) { old_advance = loader->pp2.x; @@ -228,6 +228,7 @@ } else +#endif /* !AF_USE_WARPER */ { loader->pp1.x = FT_PIX_ROUND( loader->pp1.x ); loader->pp2.x = FT_PIX_ROUND( loader->pp2.x ); diff --git a/src/autofit/aftypes.h b/src/autofit/aftypes.h index db41bd917..fbcf64014 100644 --- a/src/autofit/aftypes.h +++ b/src/autofit/aftypes.h @@ -53,6 +53,7 @@ FT_BEGIN_HEADER /*************************************************************************/ /*************************************************************************/ +#define xxAF_USE_WARPER /* only define to use warp hinting */ #define xxAF_DEBUG #ifdef AF_DEBUG diff --git a/src/autofit/afwarp.c b/src/autofit/afwarp.c new file mode 100644 index 000000000..d445cf16d --- /dev/null +++ b/src/autofit/afwarp.c @@ -0,0 +1,270 @@ +#include "afwarp.h" + +#if 1 +static const AF_WarpScore +af_warper_weights[ 64 ] = +{ + 35, 20, 20, 20, 15, 12, 10, 5, 2, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -1, -2, -5, -8,-10,-10,-20,-20,-30,-30, + + -30,-30,-20,-20,-10,-10, -8, -5, -2, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 5, 10, 12, 15, 20, 20, 20, +}; +#else +static const AF_WarpScore +af_warper_weights[ 64 ] = +{ + 30, 20, 10, 5, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -1, -2, -2, -5, -5,-10,-10,-15,-20, + + -20,-15,-15,-10,-10, -5, -5, -2, -2, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 10, 20, +}; +#endif + +static void +af_warper_compute_line_best( AF_Warper warper, + FT_Fixed scale, + FT_Pos delta, + FT_Pos xx1, + FT_Pos xx2, + AF_WarpScore base_distort, + AF_Segment segments, + FT_UInt num_segments ) +{ + FT_Int idx_min, idx_max, idx0, xx1min, xx1max; + FT_UInt nn; + AF_WarpScore scores[64]; + + for ( nn = 0; nn < 64; nn++ ) + scores[nn] = 0; + + idx0 = xx1 - warper->t1; + + /* compute minimum and maximum indices */ + { + FT_Pos xx1min = warper->x1min; + FT_Pos xx1max = warper->x1max; + FT_Pos w = xx2 - xx1; + + if ( xx1min + w < warper->x2min ) + xx1min = warper->x2min - (xx2-xx1); + + xx1max = warper->x1max; + if ( xx1max + w > warper->x2max ) + xx1max = warper->x2max - (xx2-xx1); + + idx_min = xx1min - warper->t1; + idx_max = xx1max - warper->t1; + + if ( idx_min > idx_max ) + { + printf( "invalid indices min=%d max=%d xx1=%d xx2=%d x1min=%d x1max=%d x2min=%d x2max=%d\n", + idx_min, idx_max, xx1, xx2, warper->x1min, warper->x1max, warper->x2min, warper->x2max ); + return; + } + } + + for ( nn = 0; nn < num_segments; nn++ ) + { + FT_Pos len = segments[nn].max_coord - segments[nn].min_coord; + FT_Pos y0 = FT_MulFix( segments[nn].pos, scale ) + delta; + FT_Pos y = y0 + (idx_min - idx0); + FT_Int idx; + + for ( idx = idx_min; idx <= idx_max; idx++, y++ ) + scores[idx] += af_warper_weights[ y & 63 ]*len; + } + + /* find best score */ + { + FT_Int idx; + + for ( idx = idx_min; idx <= idx_max; idx++ ) + { + AF_WarpScore score = scores[idx]; + AF_WarpScore distort = base_distort + (idx - idx0); + + if ( score > warper->best_score || + ( score == warper->best_score && + distort < warper->best_distort ) ) + { + warper->best_score = score; + warper->best_distort = distort; + warper->best_scale = scale; + warper->best_delta = delta + (idx-idx0); + } + } + } +} + + +FT_LOCAL_DEF( void ) +af_warper_compute( AF_Warper warper, + AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed *a_scale, + FT_Pos *a_delta ) +{ + AF_AxisHints axis; + AF_Point points; + FT_Fixed org_scale; + FT_Pos org_delta; + FT_UInt nn, num_points, num_segments; + FT_Int X1, X2; + FT_Int w; + AF_WarpScore base_distort; + AF_Segment segments; + + /* get original scaling transform */ + if ( dim == AF_DIMENSION_VERT ) + { + org_scale = hints->y_scale; + org_delta = hints->y_delta; + } + else + { + org_scale = hints->x_scale; + org_delta = hints->x_delta; + } + + warper->best_scale = org_scale; + warper->best_delta = org_delta; + warper->best_score = 0; + warper->best_distort = 0; + + axis = &hints->axis[dim]; + segments = axis->segments; + num_segments = axis->num_segments; + points = hints->points; + num_points = hints->num_points; + + *a_scale = org_scale; + *a_delta = org_delta; + + /* get X1 and X2, minimum and maximum in original coordinates */ + if ( axis->num_segments < 1 ) + return; + +#if 1 + X1 = X2 = points[0].fx; + for ( nn = 1; nn < num_points; nn++ ) + { + FT_Int X = points[nn].fx; + + if ( X < X1 ) + X1 = X; + if ( X > X2 ) + X2 = X; + } +#else + X1 = X2 = segments[0].pos; + for ( nn = 1; nn < num_segments; nn++ ) + { + FT_Int X = segments[nn].pos; + + if ( X < X1 ) + X1 = X; + if ( X > X2 ) + X2 = X; + } +#endif + + if ( X1 >= X2 ) + return; + + warper->x1 = FT_MulFix( X1, org_scale ) + org_delta; + warper->x2 = FT_MulFix( X2, org_scale ) + org_delta; + + warper->t1 = AF_WARPER_FLOOR(warper->x1); + warper->t2 = AF_WARPER_CEIL(warper->x2); + + warper->x1min = warper->x1 & ~31; + warper->x1max = warper->x1min + 32; + warper->x2min = warper->x2 & ~31; + warper->x2max = warper->x2min + 32; + + if ( warper->x1max > warper->x2 ) + warper->x1max = warper->x2; + + if ( warper->x2min < warper->x1 ) + warper->x2min = warper->x1; + + warper->w0 = warper->x2 - warper->x1; + + if ( warper->w0 <= 64 ) + { + warper->x1max = warper->x1; + warper->x2min = warper->x2; + } + + warper->wmin = warper->x2min - warper->x1max; + warper->wmax = warper->x2max - warper->x1min; + + if ( warper->wmin < warper->w0 - 32 ) + warper->wmin = warper->w0 - 32; + + if ( warper->wmax > warper->w0 + 32 ) + warper->wmax = warper->w0 + 32; + + if ( warper->wmin < warper->w0*3/4 ) + warper->wmin = warper->w0*3/4; + + if ( warper->wmax > warper->w0*5/4 ) + warper->wmax = warper->w0*5/4; + + + /* warper->wmin = warper->wmax = warper->w0; */ + + for ( w = warper->wmin; w <= warper->wmax; w++ ) + { + FT_Int line = w - warper->wmin; + FT_Fixed new_scale; + FT_Pos new_delta; + FT_Pos xx1, xx2; + + xx1 = warper->x1; + xx2 = warper->x2; + if ( w >= warper->w0 ) + { + xx1 -= (w-warper->w0); + if ( xx1 < warper->x1min ) + { + xx2 += warper->x1min - xx1; + xx1 = warper->x1min; + } + } + else + { + xx1 -= (w-warper->w0); + if ( xx1 > warper->x1max ) + { + xx2 -= (xx1-warper->x1max); + xx1 = warper->x1max; + } + } + + if ( xx1 < warper->x1 ) + base_distort = warper->x1 - xx1; + else + base_distort = xx1 - warper->x1; + + if ( xx2 < warper->x2 ) + base_distort += warper->x2 - xx2; + else + base_distort += xx2 - warper->x2; + + base_distort *= 10; + + new_scale = org_scale + FT_DivFix( w - warper->w0, X2-X1 ); + new_delta = xx1 - FT_MulFix( X1, new_scale ); + + af_warper_compute_line_best( warper, new_scale, new_delta, xx1, xx2, + base_distort, + segments, num_segments ); + } + + *a_scale = warper->best_scale; + *a_delta = warper->best_delta; +} + diff --git a/src/autofit/afwarp.h b/src/autofit/afwarp.h new file mode 100644 index 000000000..86af0b8ee --- /dev/null +++ b/src/autofit/afwarp.h @@ -0,0 +1,40 @@ +#ifndef __AFWARP_H__ +#define __AFWARP_H__ + +#include "afhints.h" + +FT_BEGIN_HEADER + +#define AF_WARPER_SCALE + +#define AF_WARPER_FLOOR(x) ((x) & ~63) +#define AF_WARPER_CEIL(x) AF_WARPER_FLOOR((x)+63) + +typedef FT_Int32 AF_WarpScore; + +typedef struct +{ + FT_Int X1, X2; + FT_Pos x1, x2; + FT_Pos t1, t2; + FT_Pos x1min, x1max; + FT_Pos x2min, x2max; + FT_Pos w0, wmin, wmax; + + FT_Fixed best_scale; + FT_Pos best_delta; + AF_WarpScore best_score; + AF_WarpScore best_distort; + +} AF_WarperRec, *AF_Warper; + +FT_LOCAL( void ) +af_warper_compute( AF_Warper warper, + AF_GlyphHints hints, + AF_Dimension dim, + FT_Fixed *a_scale, + FT_Fixed *a_delta ); + +FT_END_HEADER + +#endif /* __AFWARP_H__ */ diff --git a/src/autofit/autofit.c b/src/autofit/autofit.c index 2d58682ce..2526179c9 100644 --- a/src/autofit/autofit.c +++ b/src/autofit/autofit.c @@ -26,5 +26,8 @@ #include "afloader.c" #include "afmodule.c" +#ifdef AF_USE_WARPER +#include "afwarp.c" +#endif /* END */