Home | History | Annotate | Download | only in autofit
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  afcjk.c                                                                */
      4 /*                                                                         */
      5 /*    Auto-fitter hinting routines for CJK writing system (body).          */
      6 /*                                                                         */
      7 /*  Copyright 2006-2014 by                                                 */
      8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
      9 /*                                                                         */
     10 /*  This file is part of the FreeType project, and may only be used,       */
     11 /*  modified, and distributed under the terms of the FreeType project      */
     12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
     13 /*  this file you indicate that you have read the license and              */
     14 /*  understand and accept it fully.                                        */
     15 /*                                                                         */
     16 /***************************************************************************/
     17 
     18   /*
     19    *  The algorithm is based on akito's autohint patch, available here:
     20    *
     21    *  http://www.kde.gr.jp/~akito/patch/freetype2/
     22    *
     23    */
     24 
     25 #include <ft2build.h>
     26 #include FT_ADVANCES_H
     27 #include FT_INTERNAL_DEBUG_H
     28 
     29 #include "afglobal.h"
     30 #include "afpic.h"
     31 #include "aflatin.h"
     32 
     33 
     34 #ifdef AF_CONFIG_OPTION_CJK
     35 
     36 #undef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT
     37 
     38 #include "afcjk.h"
     39 #include "aferrors.h"
     40 
     41 
     42 #ifdef AF_CONFIG_OPTION_USE_WARPER
     43 #include "afwarp.h"
     44 #endif
     45 
     46 
     47   /*************************************************************************/
     48   /*                                                                       */
     49   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
     50   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
     51   /* messages during execution.                                            */
     52   /*                                                                       */
     53 #undef  FT_COMPONENT
     54 #define FT_COMPONENT  trace_afcjk
     55 
     56 
     57   /*************************************************************************/
     58   /*************************************************************************/
     59   /*****                                                               *****/
     60   /*****              C J K   G L O B A L   M E T R I C S              *****/
     61   /*****                                                               *****/
     62   /*************************************************************************/
     63   /*************************************************************************/
     64 
     65 
     66   /* Basically the Latin version with AF_CJKMetrics */
     67   /* to replace AF_LatinMetrics.                    */
     68 
     69   FT_LOCAL_DEF( void )
     70   af_cjk_metrics_init_widths( AF_CJKMetrics  metrics,
     71                               FT_Face        face )
     72   {
     73     /* scan the array of segments in each direction */
     74     AF_GlyphHintsRec  hints[1];
     75 
     76 
     77     FT_TRACE5(( "\n"
     78                 "cjk standard widths computation (style `%s')\n"
     79                 "===================================================\n"
     80                 "\n",
     81                 af_style_names[metrics->root.style_class->style] ));
     82 
     83     af_glyph_hints_init( hints, face->memory );
     84 
     85     metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
     86     metrics->axis[AF_DIMENSION_VERT].width_count = 0;
     87 
     88     {
     89       FT_Error          error;
     90       FT_ULong          glyph_index;
     91       FT_Long           y_offset;
     92       int               dim;
     93       AF_CJKMetricsRec  dummy[1];
     94       AF_Scaler         scaler = &dummy->root.scaler;
     95 
     96 #ifdef FT_CONFIG_OPTION_PIC
     97       AF_FaceGlobals  globals = metrics->root.globals;
     98 #endif
     99 
    100       AF_StyleClass   style_class  = metrics->root.style_class;
    101       AF_ScriptClass  script_class = AF_SCRIPT_CLASSES_GET
    102                                        [style_class->script];
    103 
    104       FT_UInt32  standard_char;
    105 
    106 
    107       standard_char = script_class->standard_char1;
    108       af_get_char_index( &metrics->root,
    109                          standard_char,
    110                          &glyph_index,
    111                          &y_offset );
    112       if ( !glyph_index )
    113       {
    114         if ( script_class->standard_char2 )
    115         {
    116           standard_char = script_class->standard_char2;
    117           af_get_char_index( &metrics->root,
    118                              standard_char,
    119                              &glyph_index,
    120                              &y_offset );
    121           if ( !glyph_index )
    122           {
    123             if ( script_class->standard_char3 )
    124             {
    125               standard_char = script_class->standard_char3;
    126               af_get_char_index( &metrics->root,
    127                                  standard_char,
    128                                  &glyph_index,
    129                                  &y_offset );
    130               if ( !glyph_index )
    131                 goto Exit;
    132             }
    133             else
    134               goto Exit;
    135           }
    136         }
    137         else
    138           goto Exit;
    139       }
    140 
    141       FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n",
    142                   standard_char, glyph_index ));
    143 
    144       error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
    145       if ( error || face->glyph->outline.n_points <= 0 )
    146         goto Exit;
    147 
    148       FT_ZERO( dummy );
    149 
    150       dummy->units_per_em = metrics->units_per_em;
    151 
    152       scaler->x_scale = 0x10000L;
    153       scaler->y_scale = 0x10000L;
    154       scaler->x_delta = 0;
    155       scaler->y_delta = 0;
    156 
    157       scaler->face        = face;
    158       scaler->render_mode = FT_RENDER_MODE_NORMAL;
    159       scaler->flags       = 0;
    160 
    161       af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy );
    162 
    163       error = af_glyph_hints_reload( hints, &face->glyph->outline );
    164       if ( error )
    165         goto Exit;
    166 
    167       for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
    168       {
    169         AF_CJKAxis    axis    = &metrics->axis[dim];
    170         AF_AxisHints  axhints = &hints->axis[dim];
    171         AF_Segment    seg, limit, link;
    172         FT_UInt       num_widths = 0;
    173 
    174 
    175         error = af_latin_hints_compute_segments( hints,
    176                                                  (AF_Dimension)dim );
    177         if ( error )
    178           goto Exit;
    179 
    180         af_latin_hints_link_segments( hints,
    181                                       0,
    182                                       NULL,
    183                                       (AF_Dimension)dim );
    184 
    185         seg   = axhints->segments;
    186         limit = seg + axhints->num_segments;
    187 
    188         for ( ; seg < limit; seg++ )
    189         {
    190           link = seg->link;
    191 
    192           /* we only consider stem segments there! */
    193           if ( link && link->link == seg && link > seg )
    194           {
    195             FT_Pos  dist;
    196 
    197 
    198             dist = seg->pos - link->pos;
    199             if ( dist < 0 )
    200               dist = -dist;
    201 
    202             if ( num_widths < AF_CJK_MAX_WIDTHS )
    203               axis->widths[num_widths++].org = dist;
    204           }
    205         }
    206 
    207         /* this also replaces multiple almost identical stem widths */
    208         /* with a single one (the value 100 is heuristic)           */
    209         af_sort_and_quantize_widths( &num_widths, axis->widths,
    210                                      dummy->units_per_em / 100 );
    211         axis->width_count = num_widths;
    212       }
    213 
    214     Exit:
    215       for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
    216       {
    217         AF_CJKAxis  axis = &metrics->axis[dim];
    218         FT_Pos      stdw;
    219 
    220 
    221         stdw = ( axis->width_count > 0 ) ? axis->widths[0].org
    222                                          : AF_LATIN_CONSTANT( metrics, 50 );
    223 
    224         /* let's try 20% of the smallest width */
    225         axis->edge_distance_threshold = stdw / 5;
    226         axis->standard_width          = stdw;
    227         axis->extra_light             = 0;
    228 
    229 #ifdef FT_DEBUG_LEVEL_TRACE
    230         {
    231           FT_UInt  i;
    232 
    233 
    234           FT_TRACE5(( "%s widths:\n",
    235                       dim == AF_DIMENSION_VERT ? "horizontal"
    236                                                : "vertical" ));
    237 
    238           FT_TRACE5(( "  %d (standard)", axis->standard_width ));
    239           for ( i = 1; i < axis->width_count; i++ )
    240             FT_TRACE5(( " %d", axis->widths[i].org ));
    241 
    242           FT_TRACE5(( "\n" ));
    243         }
    244 #endif
    245       }
    246     }
    247 
    248     FT_TRACE5(( "\n" ));
    249 
    250     af_glyph_hints_done( hints );
    251   }
    252 
    253 
    254   /* Find all blue zones. */
    255 
    256   static void
    257   af_cjk_metrics_init_blues( AF_CJKMetrics  metrics,
    258                              FT_Face        face )
    259   {
    260     FT_Pos      fills[AF_BLUE_STRING_MAX_LEN];
    261     FT_Pos      flats[AF_BLUE_STRING_MAX_LEN];
    262 
    263     FT_Int      num_fills;
    264     FT_Int      num_flats;
    265 
    266     FT_Bool     fill;
    267 
    268     AF_CJKBlue  blue;
    269     FT_Error    error;
    270     AF_CJKAxis  axis;
    271     FT_Outline  outline;
    272 
    273     AF_StyleClass  sc = metrics->root.style_class;
    274 
    275     AF_Blue_Stringset         bss = sc->blue_stringset;
    276     const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
    277 
    278 
    279     /* we walk over the blue character strings as specified in the   */
    280     /* style's entry in the `af_blue_stringset' array, computing its */
    281     /* extremum points (depending on the string properties)          */
    282 
    283     FT_TRACE5(( "cjk blue zones computation\n"
    284                 "==========================\n"
    285                 "\n" ));
    286 
    287     for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
    288     {
    289       const char*  p = &af_blue_strings[bs->string];
    290       FT_Pos*      blue_ref;
    291       FT_Pos*      blue_shoot;
    292 
    293 
    294       if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
    295         axis = &metrics->axis[AF_DIMENSION_HORZ];
    296       else
    297         axis = &metrics->axis[AF_DIMENSION_VERT];
    298 
    299 #ifdef FT_DEBUG_LEVEL_TRACE
    300       {
    301         FT_String*  cjk_blue_name[4] =
    302         {
    303           (FT_String*)"bottom",    /* --   , --  */
    304           (FT_String*)"top",       /* --   , TOP */
    305           (FT_String*)"left",      /* HORIZ, --  */
    306           (FT_String*)"right"      /* HORIZ, TOP */
    307         };
    308 
    309 
    310         FT_TRACE5(( "blue zone %d (%s):\n",
    311                     axis->blue_count,
    312                     cjk_blue_name[AF_CJK_IS_HORIZ_BLUE( bs ) |
    313                                   AF_CJK_IS_TOP_BLUE( bs )   ] ));
    314       }
    315 #endif /* FT_DEBUG_LEVEL_TRACE */
    316 
    317       num_fills = 0;
    318       num_flats = 0;
    319 
    320       fill = 1;  /* start with characters that define fill values */
    321       FT_TRACE5(( "  [overshoot values]\n" ));
    322 
    323       while ( *p )
    324       {
    325         FT_ULong    ch;
    326         FT_ULong    glyph_index;
    327         FT_Long     y_offset;
    328         FT_Pos      best_pos;       /* same as points.y or points.x, resp. */
    329         FT_Int      best_point;
    330         FT_Vector*  points;
    331 
    332 
    333         GET_UTF8_CHAR( ch, p );
    334 
    335         /* switch to characters that define flat values */
    336         if ( ch == '|' )
    337         {
    338           fill = 0;
    339           FT_TRACE5(( "  [reference values]\n" ));
    340           continue;
    341         }
    342 
    343         /* load the character in the face -- skip unknown or empty ones */
    344         af_get_char_index( &metrics->root, ch, &glyph_index, &y_offset );
    345         if ( glyph_index == 0 )
    346         {
    347           FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
    348           continue;
    349         }
    350 
    351         error   = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
    352         outline = face->glyph->outline;
    353         if ( error || outline.n_points <= 0 )
    354         {
    355           FT_TRACE5(( "  U+%04lX contains no outlines\n", ch ));
    356           continue;
    357         }
    358 
    359         /* now compute min or max point indices and coordinates */
    360         points     = outline.points;
    361         best_point = -1;
    362         best_pos   = 0;  /* make compiler happy */
    363 
    364         {
    365           FT_Int  nn;
    366           FT_Int  first = 0;
    367           FT_Int  last  = -1;
    368 
    369 
    370           for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ )
    371           {
    372             FT_Int  pp;
    373 
    374 
    375             last = outline.contours[nn];
    376 
    377             /* Avoid single-point contours since they are never rasterized. */
    378             /* In some fonts, they correspond to mark attachment points     */
    379             /* which are way outside of the glyph's real outline.           */
    380             if ( last <= first )
    381               continue;
    382 
    383             if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
    384             {
    385               if ( AF_CJK_IS_RIGHT_BLUE( bs ) )
    386               {
    387                 for ( pp = first; pp <= last; pp++ )
    388                   if ( best_point < 0 || points[pp].x > best_pos )
    389                   {
    390                     best_point = pp;
    391                     best_pos   = points[pp].x;
    392                   }
    393               }
    394               else
    395               {
    396                 for ( pp = first; pp <= last; pp++ )
    397                   if ( best_point < 0 || points[pp].x < best_pos )
    398                   {
    399                     best_point = pp;
    400                     best_pos   = points[pp].x;
    401                   }
    402               }
    403             }
    404             else
    405             {
    406               if ( AF_CJK_IS_TOP_BLUE( bs ) )
    407               {
    408                 for ( pp = first; pp <= last; pp++ )
    409                   if ( best_point < 0 || points[pp].y > best_pos )
    410                   {
    411                     best_point = pp;
    412                     best_pos   = points[pp].y;
    413                   }
    414               }
    415               else
    416               {
    417                 for ( pp = first; pp <= last; pp++ )
    418                   if ( best_point < 0 || points[pp].y < best_pos )
    419                   {
    420                     best_point = pp;
    421                     best_pos   = points[pp].y;
    422                   }
    423               }
    424             }
    425           }
    426 
    427           FT_TRACE5(( "  U+%04lX: best_pos = %5ld\n", ch, best_pos ));
    428         }
    429 
    430         if ( fill )
    431           fills[num_fills++] = best_pos;
    432         else
    433           flats[num_flats++] = best_pos;
    434       }
    435 
    436       if ( num_flats == 0 && num_fills == 0 )
    437       {
    438         /*
    439          *  we couldn't find a single glyph to compute this blue zone,
    440          *  we will simply ignore it then
    441          */
    442         FT_TRACE5(( "  empty\n" ));
    443         continue;
    444       }
    445 
    446       /* we have computed the contents of the `fill' and `flats' tables,   */
    447       /* now determine the reference and overshoot position of the blue -- */
    448       /* we simply take the median value after a simple sort               */
    449       af_sort_pos( num_fills, fills );
    450       af_sort_pos( num_flats, flats );
    451 
    452       blue       = &axis->blues[axis->blue_count];
    453       blue_ref   = &blue->ref.org;
    454       blue_shoot = &blue->shoot.org;
    455 
    456       axis->blue_count++;
    457 
    458       if ( num_flats == 0 )
    459       {
    460         *blue_ref   =
    461         *blue_shoot = fills[num_fills / 2];
    462       }
    463       else if ( num_fills == 0 )
    464       {
    465         *blue_ref   =
    466         *blue_shoot = flats[num_flats / 2];
    467       }
    468       else
    469       {
    470         *blue_ref   = fills[num_fills / 2];
    471         *blue_shoot = flats[num_flats / 2];
    472       }
    473 
    474       /* make sure blue_ref >= blue_shoot for top/right or */
    475       /* vice versa for bottom/left                        */
    476       if ( *blue_shoot != *blue_ref )
    477       {
    478         FT_Pos   ref       = *blue_ref;
    479         FT_Pos   shoot     = *blue_shoot;
    480         FT_Bool  under_ref = FT_BOOL( shoot < ref );
    481 
    482 
    483         /* AF_CJK_IS_TOP_BLUE covers `right' and `top' */
    484         if ( AF_CJK_IS_TOP_BLUE( bs ) ^ under_ref )
    485         {
    486           *blue_ref   =
    487           *blue_shoot = ( shoot + ref ) / 2;
    488 
    489           FT_TRACE5(( "  [reference smaller than overshoot,"
    490                       " taking mean value]\n" ));
    491         }
    492       }
    493 
    494       blue->flags = 0;
    495       if ( AF_CJK_IS_TOP_BLUE( bs ) )
    496         blue->flags |= AF_CJK_BLUE_TOP;
    497 
    498       FT_TRACE5(( "    -> reference = %ld\n"
    499                   "       overshoot = %ld\n",
    500                   *blue_ref, *blue_shoot ));
    501     }
    502 
    503     FT_TRACE5(( "\n" ));
    504 
    505     return;
    506   }
    507 
    508 
    509   /* Basically the Latin version with type AF_CJKMetrics for metrics. */
    510 
    511   FT_LOCAL_DEF( void )
    512   af_cjk_metrics_check_digits( AF_CJKMetrics  metrics,
    513                                FT_Face        face )
    514   {
    515     FT_UInt   i;
    516     FT_Bool   started = 0, same_width = 1;
    517     FT_Fixed  advance, old_advance = 0;
    518 
    519 
    520     /* digit `0' is 0x30 in all supported charmaps */
    521     for ( i = 0x30; i <= 0x39; i++ )
    522     {
    523       FT_ULong  glyph_index;
    524       FT_Long   y_offset;
    525 
    526 
    527       af_get_char_index( &metrics->root, i, &glyph_index, &y_offset );
    528       if ( glyph_index == 0 )
    529         continue;
    530 
    531       if ( FT_Get_Advance( face, glyph_index,
    532                            FT_LOAD_NO_SCALE         |
    533                            FT_LOAD_NO_HINTING       |
    534                            FT_LOAD_IGNORE_TRANSFORM,
    535                            &advance ) )
    536         continue;
    537 
    538       if ( started )
    539       {
    540         if ( advance != old_advance )
    541         {
    542           same_width = 0;
    543           break;
    544         }
    545       }
    546       else
    547       {
    548         old_advance = advance;
    549         started     = 1;
    550       }
    551     }
    552 
    553     metrics->root.digits_have_same_width = same_width;
    554   }
    555 
    556 
    557   /* Initialize global metrics. */
    558 
    559   FT_LOCAL_DEF( FT_Error )
    560   af_cjk_metrics_init( AF_CJKMetrics  metrics,
    561                        FT_Face        face )
    562   {
    563     FT_CharMap  oldmap = face->charmap;
    564 
    565 
    566     metrics->units_per_em = face->units_per_EM;
    567 
    568     if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
    569     {
    570       af_cjk_metrics_init_widths( metrics, face );
    571       af_cjk_metrics_init_blues( metrics, face );
    572       af_cjk_metrics_check_digits( metrics, face );
    573     }
    574 
    575     FT_Set_Charmap( face, oldmap );
    576     return FT_Err_Ok;
    577   }
    578 
    579 
    580   /* Adjust scaling value, then scale and shift widths   */
    581   /* and blue zones (if applicable) for given dimension. */
    582 
    583   static void
    584   af_cjk_metrics_scale_dim( AF_CJKMetrics  metrics,
    585                             AF_Scaler      scaler,
    586                             AF_Dimension   dim )
    587   {
    588     FT_Fixed    scale;
    589     FT_Pos      delta;
    590     AF_CJKAxis  axis;
    591     FT_UInt     nn;
    592 
    593 
    594     if ( dim == AF_DIMENSION_HORZ )
    595     {
    596       scale = scaler->x_scale;
    597       delta = scaler->x_delta;
    598     }
    599     else
    600     {
    601       scale = scaler->y_scale;
    602       delta = scaler->y_delta;
    603     }
    604 
    605     axis = &metrics->axis[dim];
    606 
    607     if ( axis->org_scale == scale && axis->org_delta == delta )
    608       return;
    609 
    610     axis->org_scale = scale;
    611     axis->org_delta = delta;
    612 
    613     axis->scale = scale;
    614     axis->delta = delta;
    615 
    616     /* scale the blue zones */
    617     for ( nn = 0; nn < axis->blue_count; nn++ )
    618     {
    619       AF_CJKBlue  blue = &axis->blues[nn];
    620       FT_Pos      dist;
    621 
    622 
    623       blue->ref.cur   = FT_MulFix( blue->ref.org, scale ) + delta;
    624       blue->ref.fit   = blue->ref.cur;
    625       blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
    626       blue->shoot.fit = blue->shoot.cur;
    627       blue->flags    &= ~AF_CJK_BLUE_ACTIVE;
    628 
    629       /* a blue zone is only active if it is less than 3/4 pixels tall */
    630       dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
    631       if ( dist <= 48 && dist >= -48 )
    632       {
    633         FT_Pos  delta1, delta2;
    634 
    635 
    636         blue->ref.fit  = FT_PIX_ROUND( blue->ref.cur );
    637 
    638         /* shoot is under shoot for cjk */
    639         delta1 = FT_DivFix( blue->ref.fit, scale ) - blue->shoot.org;
    640         delta2 = delta1;
    641         if ( delta1 < 0 )
    642           delta2 = -delta2;
    643 
    644         delta2 = FT_MulFix( delta2, scale );
    645 
    646         FT_TRACE5(( "delta: %d", delta1 ));
    647         if ( delta2 < 32 )
    648           delta2 = 0;
    649 #if 0
    650         else if ( delta2 < 64 )
    651           delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
    652 #endif
    653         else
    654           delta2 = FT_PIX_ROUND( delta2 );
    655         FT_TRACE5(( "/%d\n", delta2 ));
    656 
    657         if ( delta1 < 0 )
    658           delta2 = -delta2;
    659 
    660         blue->shoot.fit = blue->ref.fit - delta2;
    661 
    662         FT_TRACE5(( ">> active cjk blue zone %c%d[%ld/%ld]:\n"
    663                     "     ref:   cur=%.2f fit=%.2f\n"
    664                     "     shoot: cur=%.2f fit=%.2f\n",
    665                     ( dim == AF_DIMENSION_HORZ ) ? 'H' : 'V',
    666                     nn, blue->ref.org, blue->shoot.org,
    667                     blue->ref.cur / 64.0, blue->ref.fit / 64.0,
    668                     blue->shoot.cur / 64.0, blue->shoot.fit / 64.0 ));
    669 
    670         blue->flags |= AF_CJK_BLUE_ACTIVE;
    671       }
    672     }
    673   }
    674 
    675 
    676   /* Scale global values in both directions. */
    677 
    678   FT_LOCAL_DEF( void )
    679   af_cjk_metrics_scale( AF_CJKMetrics  metrics,
    680                         AF_Scaler      scaler )
    681   {
    682     /* we copy the whole structure since the x and y scaling values */
    683     /* are not modified, contrary to e.g. the `latin' auto-hinter   */
    684     metrics->root.scaler = *scaler;
    685 
    686     af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
    687     af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
    688   }
    689 
    690 
    691   /*************************************************************************/
    692   /*************************************************************************/
    693   /*****                                                               *****/
    694   /*****              C J K   G L Y P H   A N A L Y S I S              *****/
    695   /*****                                                               *****/
    696   /*************************************************************************/
    697   /*************************************************************************/
    698 
    699 
    700   /* Walk over all contours and compute its segments. */
    701 
    702   static FT_Error
    703   af_cjk_hints_compute_segments( AF_GlyphHints  hints,
    704                                  AF_Dimension   dim )
    705   {
    706     AF_AxisHints  axis          = &hints->axis[dim];
    707     AF_Segment    segments      = axis->segments;
    708     AF_Segment    segment_limit = segments + axis->num_segments;
    709     FT_Error      error;
    710     AF_Segment    seg;
    711 
    712 
    713     error = af_latin_hints_compute_segments( hints, dim );
    714     if ( error )
    715       return error;
    716 
    717     /* a segment is round if it doesn't have successive */
    718     /* on-curve points.                                 */
    719     for ( seg = segments; seg < segment_limit; seg++ )
    720     {
    721       AF_Point  pt   = seg->first;
    722       AF_Point  last = seg->last;
    723       AF_Flags  f0   = (AF_Flags)( pt->flags & AF_FLAG_CONTROL );
    724       AF_Flags  f1;
    725 
    726 
    727       seg->flags &= ~AF_EDGE_ROUND;
    728 
    729       for ( ; pt != last; f0 = f1 )
    730       {
    731         pt = pt->next;
    732         f1 = (AF_Flags)( pt->flags & AF_FLAG_CONTROL );
    733 
    734         if ( !f0 && !f1 )
    735           break;
    736 
    737         if ( pt == last )
    738           seg->flags |= AF_EDGE_ROUND;
    739       }
    740     }
    741 
    742     return FT_Err_Ok;
    743   }
    744 
    745 
    746   static void
    747   af_cjk_hints_link_segments( AF_GlyphHints  hints,
    748                               AF_Dimension   dim )
    749   {
    750     AF_AxisHints  axis          = &hints->axis[dim];
    751     AF_Segment    segments      = axis->segments;
    752     AF_Segment    segment_limit = segments + axis->num_segments;
    753     AF_Direction  major_dir     = axis->major_dir;
    754     AF_Segment    seg1, seg2;
    755     FT_Pos        len_threshold;
    756     FT_Pos        dist_threshold;
    757 
    758 
    759     len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
    760 
    761     dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
    762                                                   : hints->y_scale;
    763     dist_threshold = FT_DivFix( 64 * 3, dist_threshold );
    764 
    765     /* now compare each segment to the others */
    766     for ( seg1 = segments; seg1 < segment_limit; seg1++ )
    767     {
    768       /* the fake segments are for metrics hinting only */
    769       if ( seg1->first == seg1->last )
    770         continue;
    771 
    772       if ( seg1->dir != major_dir )
    773         continue;
    774 
    775       for ( seg2 = segments; seg2 < segment_limit; seg2++ )
    776         if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
    777         {
    778           FT_Pos  dist = seg2->pos - seg1->pos;
    779 
    780 
    781           if ( dist < 0 )
    782             continue;
    783 
    784           {
    785             FT_Pos  min = seg1->min_coord;
    786             FT_Pos  max = seg1->max_coord;
    787             FT_Pos  len;
    788 
    789 
    790             if ( min < seg2->min_coord )
    791               min = seg2->min_coord;
    792 
    793             if ( max > seg2->max_coord )
    794               max = seg2->max_coord;
    795 
    796             len = max - min;
    797             if ( len >= len_threshold )
    798             {
    799               if ( dist * 8 < seg1->score * 9                        &&
    800                    ( dist * 8 < seg1->score * 7 || seg1->len < len ) )
    801               {
    802                 seg1->score = dist;
    803                 seg1->len   = len;
    804                 seg1->link  = seg2;
    805               }
    806 
    807               if ( dist * 8 < seg2->score * 9                        &&
    808                    ( dist * 8 < seg2->score * 7 || seg2->len < len ) )
    809               {
    810                 seg2->score = dist;
    811                 seg2->len   = len;
    812                 seg2->link  = seg1;
    813               }
    814             }
    815           }
    816         }
    817     }
    818 
    819     /*
    820      *  now compute the `serif' segments
    821      *
    822      *  In Hanzi, some strokes are wider on one or both of the ends.
    823      *  We either identify the stems on the ends as serifs or remove
    824      *  the linkage, depending on the length of the stems.
    825      *
    826      */
    827 
    828     {
    829       AF_Segment  link1, link2;
    830 
    831 
    832       for ( seg1 = segments; seg1 < segment_limit; seg1++ )
    833       {
    834         link1 = seg1->link;
    835         if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos )
    836           continue;
    837 
    838         if ( seg1->score >= dist_threshold )
    839           continue;
    840 
    841         for ( seg2 = segments; seg2 < segment_limit; seg2++ )
    842         {
    843           if ( seg2->pos > seg1->pos || seg1 == seg2 )
    844             continue;
    845 
    846           link2 = seg2->link;
    847           if ( !link2 || link2->link != seg2 || link2->pos < link1->pos )
    848             continue;
    849 
    850           if ( seg1->pos == seg2->pos && link1->pos == link2->pos )
    851             continue;
    852 
    853           if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score )
    854             continue;
    855 
    856           /* seg2 < seg1 < link1 < link2 */
    857 
    858           if ( seg1->len >= seg2->len * 3 )
    859           {
    860             AF_Segment  seg;
    861 
    862 
    863             for ( seg = segments; seg < segment_limit; seg++ )
    864             {
    865               AF_Segment  link = seg->link;
    866 
    867 
    868               if ( link == seg2 )
    869               {
    870                 seg->link  = 0;
    871                 seg->serif = link1;
    872               }
    873               else if ( link == link2 )
    874               {
    875                 seg->link  = 0;
    876                 seg->serif = seg1;
    877               }
    878             }
    879           }
    880           else
    881           {
    882             seg1->link = link1->link = 0;
    883 
    884             break;
    885           }
    886         }
    887       }
    888     }
    889 
    890     for ( seg1 = segments; seg1 < segment_limit; seg1++ )
    891     {
    892       seg2 = seg1->link;
    893 
    894       if ( seg2 )
    895       {
    896         seg2->num_linked++;
    897         if ( seg2->link != seg1 )
    898         {
    899           seg1->link = 0;
    900 
    901           if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 )
    902             seg1->serif = seg2->link;
    903           else
    904             seg2->num_linked--;
    905         }
    906       }
    907     }
    908   }
    909 
    910 
    911   static FT_Error
    912   af_cjk_hints_compute_edges( AF_GlyphHints  hints,
    913                               AF_Dimension   dim )
    914   {
    915     AF_AxisHints  axis   = &hints->axis[dim];
    916     FT_Error      error  = FT_Err_Ok;
    917     FT_Memory     memory = hints->memory;
    918     AF_CJKAxis    laxis  = &((AF_CJKMetrics)hints->metrics)->axis[dim];
    919 
    920     AF_Segment    segments      = axis->segments;
    921     AF_Segment    segment_limit = segments + axis->num_segments;
    922     AF_Segment    seg;
    923 
    924     FT_Fixed      scale;
    925     FT_Pos        edge_distance_threshold;
    926 
    927 
    928     axis->num_edges = 0;
    929 
    930     scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
    931                                          : hints->y_scale;
    932 
    933     /*********************************************************************/
    934     /*                                                                   */
    935     /* We begin by generating a sorted table of edges for the current    */
    936     /* direction.  To do so, we simply scan each segment and try to find */
    937     /* an edge in our table that corresponds to its position.            */
    938     /*                                                                   */
    939     /* If no edge is found, we create and insert a new edge in the       */
    940     /* sorted table.  Otherwise, we simply add the segment to the edge's */
    941     /* list which is then processed in the second step to compute the    */
    942     /* edge's properties.                                                */
    943     /*                                                                   */
    944     /* Note that the edges table is sorted along the segment/edge        */
    945     /* position.                                                         */
    946     /*                                                                   */
    947     /*********************************************************************/
    948 
    949     edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
    950                                          scale );
    951     if ( edge_distance_threshold > 64 / 4 )
    952       edge_distance_threshold = FT_DivFix( 64 / 4, scale );
    953     else
    954       edge_distance_threshold = laxis->edge_distance_threshold;
    955 
    956     for ( seg = segments; seg < segment_limit; seg++ )
    957     {
    958       AF_Edge  found = NULL;
    959       FT_Pos   best  = 0xFFFFU;
    960       FT_Int   ee;
    961 
    962 
    963       /* look for an edge corresponding to the segment */
    964       for ( ee = 0; ee < axis->num_edges; ee++ )
    965       {
    966         AF_Edge  edge = axis->edges + ee;
    967         FT_Pos   dist;
    968 
    969 
    970         if ( edge->dir != seg->dir )
    971           continue;
    972 
    973         dist = seg->pos - edge->fpos;
    974         if ( dist < 0 )
    975           dist = -dist;
    976 
    977         if ( dist < edge_distance_threshold && dist < best )
    978         {
    979           AF_Segment  link = seg->link;
    980 
    981 
    982           /* check whether all linked segments of the candidate edge */
    983           /* can make a single edge.                                 */
    984           if ( link )
    985           {
    986             AF_Segment  seg1  = edge->first;
    987             FT_Pos      dist2 = 0;
    988 
    989 
    990             do
    991             {
    992               AF_Segment  link1 = seg1->link;
    993 
    994 
    995               if ( link1 )
    996               {
    997                 dist2 = AF_SEGMENT_DIST( link, link1 );
    998                 if ( dist2 >= edge_distance_threshold )
    999                   break;
   1000               }
   1001 
   1002             } while ( ( seg1 = seg1->edge_next ) != edge->first );
   1003 
   1004             if ( dist2 >= edge_distance_threshold )
   1005               continue;
   1006           }
   1007 
   1008           best  = dist;
   1009           found = edge;
   1010         }
   1011       }
   1012 
   1013       if ( !found )
   1014       {
   1015         AF_Edge  edge;
   1016 
   1017 
   1018         /* insert a new edge in the list and */
   1019         /* sort according to the position    */
   1020         error = af_axis_hints_new_edge( axis, seg->pos,
   1021                                         (AF_Direction)seg->dir,
   1022                                         memory, &edge );
   1023         if ( error )
   1024           goto Exit;
   1025 
   1026         /* add the segment to the new edge's list */
   1027         FT_ZERO( edge );
   1028 
   1029         edge->first    = seg;
   1030         edge->last     = seg;
   1031         edge->fpos     = seg->pos;
   1032         edge->opos     = edge->pos = FT_MulFix( seg->pos, scale );
   1033         seg->edge_next = seg;
   1034         edge->dir      = seg->dir;
   1035       }
   1036       else
   1037       {
   1038         /* if an edge was found, simply add the segment to the edge's */
   1039         /* list                                                       */
   1040         seg->edge_next         = found->first;
   1041         found->last->edge_next = seg;
   1042         found->last            = seg;
   1043       }
   1044     }
   1045 
   1046     /******************************************************************/
   1047     /*                                                                */
   1048     /* Good, we now compute each edge's properties according to the   */
   1049     /* segments found on its position.  Basically, these are          */
   1050     /*                                                                */
   1051     /*  - the edge's main direction                                   */
   1052     /*  - stem edge, serif edge or both (which defaults to stem then) */
   1053     /*  - rounded edge, straight or both (which defaults to straight) */
   1054     /*  - link for edge                                               */
   1055     /*                                                                */
   1056     /******************************************************************/
   1057 
   1058     /* first of all, set the `edge' field in each segment -- this is */
   1059     /* required in order to compute edge links                       */
   1060 
   1061     /*
   1062      * Note that removing this loop and setting the `edge' field of each
   1063      * segment directly in the code above slows down execution speed for
   1064      * some reasons on platforms like the Sun.
   1065      */
   1066     {
   1067       AF_Edge  edges      = axis->edges;
   1068       AF_Edge  edge_limit = edges + axis->num_edges;
   1069       AF_Edge  edge;
   1070 
   1071 
   1072       for ( edge = edges; edge < edge_limit; edge++ )
   1073       {
   1074         seg = edge->first;
   1075         if ( seg )
   1076           do
   1077           {
   1078             seg->edge = edge;
   1079             seg       = seg->edge_next;
   1080 
   1081           } while ( seg != edge->first );
   1082       }
   1083 
   1084       /* now compute each edge properties */
   1085       for ( edge = edges; edge < edge_limit; edge++ )
   1086       {
   1087         FT_Int  is_round    = 0;  /* does it contain round segments?    */
   1088         FT_Int  is_straight = 0;  /* does it contain straight segments? */
   1089 
   1090 
   1091         seg = edge->first;
   1092 
   1093         do
   1094         {
   1095           FT_Bool  is_serif;
   1096 
   1097 
   1098           /* check for roundness of segment */
   1099           if ( seg->flags & AF_EDGE_ROUND )
   1100             is_round++;
   1101           else
   1102             is_straight++;
   1103 
   1104           /* check for links -- if seg->serif is set, then seg->link must */
   1105           /* be ignored                                                   */
   1106           is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
   1107 
   1108           if ( seg->link || is_serif )
   1109           {
   1110             AF_Edge     edge2;
   1111             AF_Segment  seg2;
   1112 
   1113 
   1114             edge2 = edge->link;
   1115             seg2  = seg->link;
   1116 
   1117             if ( is_serif )
   1118             {
   1119               seg2  = seg->serif;
   1120               edge2 = edge->serif;
   1121             }
   1122 
   1123             if ( edge2 )
   1124             {
   1125               FT_Pos  edge_delta;
   1126               FT_Pos  seg_delta;
   1127 
   1128 
   1129               edge_delta = edge->fpos - edge2->fpos;
   1130               if ( edge_delta < 0 )
   1131                 edge_delta = -edge_delta;
   1132 
   1133               seg_delta = AF_SEGMENT_DIST( seg, seg2 );
   1134 
   1135               if ( seg_delta < edge_delta )
   1136                 edge2 = seg2->edge;
   1137             }
   1138             else
   1139               edge2 = seg2->edge;
   1140 
   1141             if ( is_serif )
   1142             {
   1143               edge->serif   = edge2;
   1144               edge2->flags |= AF_EDGE_SERIF;
   1145             }
   1146             else
   1147               edge->link  = edge2;
   1148           }
   1149 
   1150           seg = seg->edge_next;
   1151 
   1152         } while ( seg != edge->first );
   1153 
   1154         /* set the round/straight flags */
   1155         edge->flags = AF_EDGE_NORMAL;
   1156 
   1157         if ( is_round > 0 && is_round >= is_straight )
   1158           edge->flags |= AF_EDGE_ROUND;
   1159 
   1160         /* get rid of serifs if link is set                 */
   1161         /* XXX: This gets rid of many unpleasant artefacts! */
   1162         /*      Example: the `c' in cour.pfa at size 13     */
   1163 
   1164         if ( edge->serif && edge->link )
   1165           edge->serif = 0;
   1166       }
   1167     }
   1168 
   1169   Exit:
   1170     return error;
   1171   }
   1172 
   1173 
   1174   /* Detect segments and edges for given dimension. */
   1175 
   1176   static FT_Error
   1177   af_cjk_hints_detect_features( AF_GlyphHints  hints,
   1178                                 AF_Dimension   dim )
   1179   {
   1180     FT_Error  error;
   1181 
   1182 
   1183     error = af_cjk_hints_compute_segments( hints, dim );
   1184     if ( !error )
   1185     {
   1186       af_cjk_hints_link_segments( hints, dim );
   1187 
   1188       error = af_cjk_hints_compute_edges( hints, dim );
   1189     }
   1190     return error;
   1191   }
   1192 
   1193 
   1194   /* Compute all edges which lie within blue zones. */
   1195 
   1196   FT_LOCAL_DEF( void )
   1197   af_cjk_hints_compute_blue_edges( AF_GlyphHints  hints,
   1198                                    AF_CJKMetrics  metrics,
   1199                                    AF_Dimension   dim )
   1200   {
   1201     AF_AxisHints  axis       = &hints->axis[dim];
   1202     AF_Edge       edge       = axis->edges;
   1203     AF_Edge       edge_limit = edge + axis->num_edges;
   1204     AF_CJKAxis    cjk        = &metrics->axis[dim];
   1205     FT_Fixed      scale      = cjk->scale;
   1206     FT_Pos        best_dist0;  /* initial threshold */
   1207 
   1208 
   1209     /* compute the initial threshold as a fraction of the EM size */
   1210     best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale );
   1211 
   1212     if ( best_dist0 > 64 / 2 ) /* maximum 1/2 pixel */
   1213       best_dist0 = 64 / 2;
   1214 
   1215     /* compute which blue zones are active, i.e. have their scaled */
   1216     /* size < 3/4 pixels                                           */
   1217 
   1218     /* If the distant between an edge and a blue zone is shorter than */
   1219     /* best_dist0, set the blue zone for the edge.  Then search for   */
   1220     /* the blue zone with the smallest best_dist to the edge.         */
   1221 
   1222     for ( ; edge < edge_limit; edge++ )
   1223     {
   1224       FT_UInt   bb;
   1225       AF_Width  best_blue = NULL;
   1226       FT_Pos    best_dist = best_dist0;
   1227 
   1228 
   1229       for ( bb = 0; bb < cjk->blue_count; bb++ )
   1230       {
   1231         AF_CJKBlue  blue = cjk->blues + bb;
   1232         FT_Bool     is_top_right_blue, is_major_dir;
   1233 
   1234 
   1235         /* skip inactive blue zones (i.e., those that are too small) */
   1236         if ( !( blue->flags & AF_CJK_BLUE_ACTIVE ) )
   1237           continue;
   1238 
   1239         /* if it is a top zone, check for right edges -- if it is a bottom */
   1240         /* zone, check for left edges                                      */
   1241         /*                                                                 */
   1242         /* of course, that's for TrueType                                  */
   1243         is_top_right_blue =
   1244           (FT_Byte)( ( blue->flags & AF_CJK_BLUE_TOP ) != 0 );
   1245         is_major_dir =
   1246           FT_BOOL( edge->dir == axis->major_dir );
   1247 
   1248         /* if it is a top zone, the edge must be against the major    */
   1249         /* direction; if it is a bottom zone, it must be in the major */
   1250         /* direction                                                  */
   1251         if ( is_top_right_blue ^ is_major_dir )
   1252         {
   1253           FT_Pos    dist;
   1254           AF_Width  compare;
   1255 
   1256 
   1257           /* Compare the edge to the closest blue zone type */
   1258           if ( FT_ABS( edge->fpos - blue->ref.org ) >
   1259                FT_ABS( edge->fpos - blue->shoot.org ) )
   1260             compare = &blue->shoot;
   1261           else
   1262             compare = &blue->ref;
   1263 
   1264           dist = edge->fpos - compare->org;
   1265           if ( dist < 0 )
   1266             dist = -dist;
   1267 
   1268           dist = FT_MulFix( dist, scale );
   1269           if ( dist < best_dist )
   1270           {
   1271             best_dist = dist;
   1272             best_blue = compare;
   1273           }
   1274         }
   1275       }
   1276 
   1277       if ( best_blue )
   1278         edge->blue_edge = best_blue;
   1279     }
   1280   }
   1281 
   1282 
   1283   /* Initalize hinting engine. */
   1284 
   1285   FT_LOCAL_DEF( FT_Error )
   1286   af_cjk_hints_init( AF_GlyphHints  hints,
   1287                      AF_CJKMetrics  metrics )
   1288   {
   1289     FT_Render_Mode  mode;
   1290     FT_UInt32       scaler_flags, other_flags;
   1291 
   1292 
   1293     af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics );
   1294 
   1295     /*
   1296      *  correct x_scale and y_scale when needed, since they may have
   1297      *  been modified af_cjk_scale_dim above
   1298      */
   1299     hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
   1300     hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
   1301     hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
   1302     hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
   1303 
   1304     /* compute flags depending on render mode, etc. */
   1305     mode = metrics->root.scaler.render_mode;
   1306 
   1307 #ifdef AF_CONFIG_OPTION_USE_WARPER
   1308     if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
   1309       metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
   1310 #endif
   1311 
   1312     scaler_flags = hints->scaler_flags;
   1313     other_flags  = 0;
   1314 
   1315     /*
   1316      *  We snap the width of vertical stems for the monochrome and
   1317      *  horizontal LCD rendering targets only.
   1318      */
   1319     if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
   1320       other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
   1321 
   1322     /*
   1323      *  We snap the width of horizontal stems for the monochrome and
   1324      *  vertical LCD rendering targets only.
   1325      */
   1326     if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
   1327       other_flags |= AF_LATIN_HINTS_VERT_SNAP;
   1328 
   1329     /*
   1330      *  We adjust stems to full pixels only if we don't use the `light' mode.
   1331      */
   1332     if ( mode != FT_RENDER_MODE_LIGHT )
   1333       other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
   1334 
   1335     if ( mode == FT_RENDER_MODE_MONO )
   1336       other_flags |= AF_LATIN_HINTS_MONO;
   1337 
   1338     scaler_flags |= AF_SCALER_FLAG_NO_ADVANCE;
   1339 
   1340     hints->scaler_flags = scaler_flags;
   1341     hints->other_flags  = other_flags;
   1342 
   1343     return FT_Err_Ok;
   1344   }
   1345 
   1346 
   1347   /*************************************************************************/
   1348   /*************************************************************************/
   1349   /*****                                                               *****/
   1350   /*****          C J K   G L Y P H   G R I D - F I T T I N G          *****/
   1351   /*****                                                               *****/
   1352   /*************************************************************************/
   1353   /*************************************************************************/
   1354 
   1355   /* Snap a given width in scaled coordinates to one of the */
   1356   /* current standard widths.                               */
   1357 
   1358   static FT_Pos
   1359   af_cjk_snap_width( AF_Width  widths,
   1360                      FT_Int    count,
   1361                      FT_Pos    width )
   1362   {
   1363     int     n;
   1364     FT_Pos  best      = 64 + 32 + 2;
   1365     FT_Pos  reference = width;
   1366     FT_Pos  scaled;
   1367 
   1368 
   1369     for ( n = 0; n < count; n++ )
   1370     {
   1371       FT_Pos  w;
   1372       FT_Pos  dist;
   1373 
   1374 
   1375       w = widths[n].cur;
   1376       dist = width - w;
   1377       if ( dist < 0 )
   1378         dist = -dist;
   1379       if ( dist < best )
   1380       {
   1381         best      = dist;
   1382         reference = w;
   1383       }
   1384     }
   1385 
   1386     scaled = FT_PIX_ROUND( reference );
   1387 
   1388     if ( width >= reference )
   1389     {
   1390       if ( width < scaled + 48 )
   1391         width = reference;
   1392     }
   1393     else
   1394     {
   1395       if ( width > scaled - 48 )
   1396         width = reference;
   1397     }
   1398 
   1399     return width;
   1400   }
   1401 
   1402 
   1403   /* Compute the snapped width of a given stem.                          */
   1404   /* There is a lot of voodoo in this function; changing the hard-coded  */
   1405   /* parameters influence the whole hinting process.                     */
   1406 
   1407   static FT_Pos
   1408   af_cjk_compute_stem_width( AF_GlyphHints  hints,
   1409                              AF_Dimension   dim,
   1410                              FT_Pos         width,
   1411                              AF_Edge_Flags  base_flags,
   1412                              AF_Edge_Flags  stem_flags )
   1413   {
   1414     AF_CJKMetrics  metrics  = (AF_CJKMetrics)hints->metrics;
   1415     AF_CJKAxis     axis     = &metrics->axis[dim];
   1416     FT_Pos         dist     = width;
   1417     FT_Int         sign     = 0;
   1418     FT_Bool        vertical = FT_BOOL( dim == AF_DIMENSION_VERT );
   1419 
   1420     FT_UNUSED( base_flags );
   1421     FT_UNUSED( stem_flags );
   1422 
   1423 
   1424     if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
   1425       return width;
   1426 
   1427     if ( dist < 0 )
   1428     {
   1429       dist = -width;
   1430       sign = 1;
   1431     }
   1432 
   1433     if ( (  vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
   1434          ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
   1435     {
   1436       /* smooth hinting process: very lightly quantize the stem width */
   1437 
   1438       if ( axis->width_count > 0 )
   1439       {
   1440         if ( FT_ABS( dist - axis->widths[0].cur ) < 40 )
   1441         {
   1442           dist = axis->widths[0].cur;
   1443           if ( dist < 48 )
   1444             dist = 48;
   1445 
   1446           goto Done_Width;
   1447         }
   1448       }
   1449 
   1450       if ( dist < 54 )
   1451         dist += ( 54 - dist ) / 2 ;
   1452       else if ( dist < 3 * 64 )
   1453       {
   1454         FT_Pos  delta;
   1455 
   1456 
   1457         delta  = dist & 63;
   1458         dist  &= -64;
   1459 
   1460         if ( delta < 10 )
   1461           dist += delta;
   1462         else if ( delta < 22 )
   1463           dist += 10;
   1464         else if ( delta < 42 )
   1465           dist += delta;
   1466         else if ( delta < 54 )
   1467           dist += 54;
   1468         else
   1469           dist += delta;
   1470       }
   1471     }
   1472     else
   1473     {
   1474       /* strong hinting process: snap the stem width to integer pixels */
   1475 
   1476       dist = af_cjk_snap_width( axis->widths, axis->width_count, dist );
   1477 
   1478       if ( vertical )
   1479       {
   1480         /* in the case of vertical hinting, always round */
   1481         /* the stem heights to integer pixels            */
   1482 
   1483         if ( dist >= 64 )
   1484           dist = ( dist + 16 ) & ~63;
   1485         else
   1486           dist = 64;
   1487       }
   1488       else
   1489       {
   1490         if ( AF_LATIN_HINTS_DO_MONO( hints ) )
   1491         {
   1492           /* monochrome horizontal hinting: snap widths to integer pixels */
   1493           /* with a different threshold                                   */
   1494 
   1495           if ( dist < 64 )
   1496             dist = 64;
   1497           else
   1498             dist = ( dist + 32 ) & ~63;
   1499         }
   1500         else
   1501         {
   1502           /* for horizontal anti-aliased hinting, we adopt a more subtle */
   1503           /* approach: we strengthen small stems, round stems whose size */
   1504           /* is between 1 and 2 pixels to an integer, otherwise nothing  */
   1505 
   1506           if ( dist < 48 )
   1507             dist = ( dist + 64 ) >> 1;
   1508 
   1509           else if ( dist < 128 )
   1510             dist = ( dist + 22 ) & ~63;
   1511           else
   1512             /* round otherwise to prevent color fringes in LCD mode */
   1513             dist = ( dist + 32 ) & ~63;
   1514         }
   1515       }
   1516     }
   1517 
   1518   Done_Width:
   1519     if ( sign )
   1520       dist = -dist;
   1521 
   1522     return dist;
   1523   }
   1524 
   1525 
   1526   /* Align one stem edge relative to the previous stem edge. */
   1527 
   1528   static void
   1529   af_cjk_align_linked_edge( AF_GlyphHints  hints,
   1530                             AF_Dimension   dim,
   1531                             AF_Edge        base_edge,
   1532                             AF_Edge        stem_edge )
   1533   {
   1534     FT_Pos  dist = stem_edge->opos - base_edge->opos;
   1535 
   1536     FT_Pos  fitted_width = af_cjk_compute_stem_width(
   1537                              hints, dim, dist,
   1538                              (AF_Edge_Flags)base_edge->flags,
   1539                              (AF_Edge_Flags)stem_edge->flags );
   1540 
   1541 
   1542     stem_edge->pos = base_edge->pos + fitted_width;
   1543 
   1544     FT_TRACE5(( "  CJKLINK: edge %d @%d (opos=%.2f) linked to %.2f,"
   1545                 " dist was %.2f, now %.2f\n",
   1546                 stem_edge - hints->axis[dim].edges, stem_edge->fpos,
   1547                 stem_edge->opos / 64.0, stem_edge->pos / 64.0,
   1548                 dist / 64.0, fitted_width / 64.0 ));
   1549   }
   1550 
   1551 
   1552   /* Shift the coordinates of the `serif' edge by the same amount */
   1553   /* as the corresponding `base' edge has been moved already.     */
   1554 
   1555   static void
   1556   af_cjk_align_serif_edge( AF_GlyphHints  hints,
   1557                            AF_Edge        base,
   1558                            AF_Edge        serif )
   1559   {
   1560     FT_UNUSED( hints );
   1561 
   1562     serif->pos = base->pos + ( serif->opos - base->opos );
   1563   }
   1564 
   1565 
   1566   /*************************************************************************/
   1567   /*************************************************************************/
   1568   /*************************************************************************/
   1569   /****                                                                 ****/
   1570   /****                    E D G E   H I N T I N G                      ****/
   1571   /****                                                                 ****/
   1572   /*************************************************************************/
   1573   /*************************************************************************/
   1574   /*************************************************************************/
   1575 
   1576 
   1577 #define AF_LIGHT_MODE_MAX_HORZ_GAP    9
   1578 #define AF_LIGHT_MODE_MAX_VERT_GAP   15
   1579 #define AF_LIGHT_MODE_MAX_DELTA_ABS  14
   1580 
   1581 
   1582   static FT_Pos
   1583   af_hint_normal_stem( AF_GlyphHints  hints,
   1584                        AF_Edge        edge,
   1585                        AF_Edge        edge2,
   1586                        FT_Pos         anchor,
   1587                        AF_Dimension   dim )
   1588   {
   1589     FT_Pos  org_len, cur_len, org_center;
   1590     FT_Pos  cur_pos1, cur_pos2;
   1591     FT_Pos  d_off1, u_off1, d_off2, u_off2, delta;
   1592     FT_Pos  offset;
   1593     FT_Pos  threshold = 64;
   1594 
   1595 
   1596     if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
   1597     {
   1598       if ( ( edge->flags  & AF_EDGE_ROUND ) &&
   1599            ( edge2->flags & AF_EDGE_ROUND ) )
   1600       {
   1601         if ( dim == AF_DIMENSION_VERT )
   1602           threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP;
   1603         else
   1604           threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP;
   1605       }
   1606       else
   1607       {
   1608         if ( dim == AF_DIMENSION_VERT )
   1609           threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP / 3;
   1610         else
   1611           threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP / 3;
   1612       }
   1613     }
   1614 
   1615     org_len    = edge2->opos - edge->opos;
   1616     cur_len    = af_cjk_compute_stem_width( hints, dim, org_len,
   1617                                             (AF_Edge_Flags)edge->flags,
   1618                                             (AF_Edge_Flags)edge2->flags );
   1619 
   1620     org_center = ( edge->opos + edge2->opos ) / 2 + anchor;
   1621     cur_pos1   = org_center - cur_len / 2;
   1622     cur_pos2   = cur_pos1 + cur_len;
   1623     d_off1     = cur_pos1 - FT_PIX_FLOOR( cur_pos1 );
   1624     d_off2     = cur_pos2 - FT_PIX_FLOOR( cur_pos2 );
   1625     u_off1     = 64 - d_off1;
   1626     u_off2     = 64 - d_off2;
   1627     delta      = 0;
   1628 
   1629 
   1630     if ( d_off1 == 0 || d_off2 == 0 )
   1631       goto Exit;
   1632 
   1633     if ( cur_len <= threshold )
   1634     {
   1635       if ( d_off2 < cur_len )
   1636       {
   1637         if ( u_off1 <= d_off2 )
   1638           delta =  u_off1;
   1639         else
   1640           delta = -d_off2;
   1641       }
   1642 
   1643       goto Exit;
   1644     }
   1645 
   1646     if ( threshold < 64 )
   1647     {
   1648       if ( d_off1 >= threshold || u_off1 >= threshold ||
   1649            d_off2 >= threshold || u_off2 >= threshold )
   1650         goto Exit;
   1651     }
   1652 
   1653     offset = cur_len & 63;
   1654 
   1655     if ( offset < 32 )
   1656     {
   1657       if ( u_off1 <= offset || d_off2 <= offset )
   1658         goto Exit;
   1659     }
   1660     else
   1661       offset = 64 - threshold;
   1662 
   1663     d_off1 = threshold - u_off1;
   1664     u_off1 = u_off1    - offset;
   1665     u_off2 = threshold - d_off2;
   1666     d_off2 = d_off2    - offset;
   1667 
   1668     if ( d_off1 <= u_off1 )
   1669       u_off1 = -d_off1;
   1670 
   1671     if ( d_off2 <= u_off2 )
   1672       u_off2 = -d_off2;
   1673 
   1674     if ( FT_ABS( u_off1 ) <= FT_ABS( u_off2 ) )
   1675       delta = u_off1;
   1676     else
   1677       delta = u_off2;
   1678 
   1679   Exit:
   1680 
   1681 #if 1
   1682     if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
   1683     {
   1684       if ( delta > AF_LIGHT_MODE_MAX_DELTA_ABS )
   1685         delta = AF_LIGHT_MODE_MAX_DELTA_ABS;
   1686       else if ( delta < -AF_LIGHT_MODE_MAX_DELTA_ABS )
   1687         delta = -AF_LIGHT_MODE_MAX_DELTA_ABS;
   1688     }
   1689 #endif
   1690 
   1691     cur_pos1 += delta;
   1692 
   1693     if ( edge->opos < edge2->opos )
   1694     {
   1695       edge->pos  = cur_pos1;
   1696       edge2->pos = cur_pos1 + cur_len;
   1697     }
   1698     else
   1699     {
   1700       edge->pos  = cur_pos1 + cur_len;
   1701       edge2->pos = cur_pos1;
   1702     }
   1703 
   1704     return delta;
   1705   }
   1706 
   1707 
   1708   /* The main grid-fitting routine. */
   1709 
   1710   static void
   1711   af_cjk_hint_edges( AF_GlyphHints  hints,
   1712                      AF_Dimension   dim )
   1713   {
   1714     AF_AxisHints  axis       = &hints->axis[dim];
   1715     AF_Edge       edges      = axis->edges;
   1716     AF_Edge       edge_limit = edges + axis->num_edges;
   1717     FT_PtrDist    n_edges;
   1718     AF_Edge       edge;
   1719     AF_Edge       anchor   = 0;
   1720     FT_Pos        delta    = 0;
   1721     FT_Int        skipped  = 0;
   1722     FT_Bool       has_last_stem = FALSE;
   1723     FT_Pos        last_stem_pos = 0;
   1724 
   1725 #ifdef FT_DEBUG_LEVEL_TRACE
   1726     FT_UInt       num_actions = 0;
   1727 #endif
   1728 
   1729 
   1730     FT_TRACE5(( "cjk %s edge hinting (style `%s')\n",
   1731                 dim == AF_DIMENSION_VERT ? "horizontal" : "vertical",
   1732                 af_style_names[hints->metrics->style_class->style] ));
   1733 
   1734     /* we begin by aligning all stems relative to the blue zone */
   1735 
   1736     if ( AF_HINTS_DO_BLUES( hints ) )
   1737     {
   1738       for ( edge = edges; edge < edge_limit; edge++ )
   1739       {
   1740         AF_Width  blue;
   1741         AF_Edge   edge1, edge2;
   1742 
   1743 
   1744         if ( edge->flags & AF_EDGE_DONE )
   1745           continue;
   1746 
   1747         blue  = edge->blue_edge;
   1748         edge1 = NULL;
   1749         edge2 = edge->link;
   1750 
   1751         if ( blue )
   1752         {
   1753           edge1 = edge;
   1754         }
   1755         else if ( edge2 && edge2->blue_edge )
   1756         {
   1757           blue  = edge2->blue_edge;
   1758           edge1 = edge2;
   1759           edge2 = edge;
   1760         }
   1761 
   1762         if ( !edge1 )
   1763           continue;
   1764 
   1765 #ifdef FT_DEBUG_LEVEL_TRACE
   1766         FT_TRACE5(( "  CJKBLUE: edge %d @%d (opos=%.2f) snapped to %.2f,"
   1767                     " was %.2f\n",
   1768                     edge1 - edges, edge1->fpos, edge1->opos / 64.0,
   1769                     blue->fit / 64.0, edge1->pos / 64.0 ));
   1770 
   1771         num_actions++;
   1772 #endif
   1773 
   1774         edge1->pos    = blue->fit;
   1775         edge1->flags |= AF_EDGE_DONE;
   1776 
   1777         if ( edge2 && !edge2->blue_edge )
   1778         {
   1779           af_cjk_align_linked_edge( hints, dim, edge1, edge2 );
   1780           edge2->flags |= AF_EDGE_DONE;
   1781 
   1782 #ifdef FT_DEBUG_LEVEL_TRACE
   1783           num_actions++;
   1784 #endif
   1785         }
   1786 
   1787         if ( !anchor )
   1788           anchor = edge;
   1789       }
   1790     }
   1791 
   1792     /* now we align all stem edges. */
   1793     for ( edge = edges; edge < edge_limit; edge++ )
   1794     {
   1795       AF_Edge  edge2;
   1796 
   1797 
   1798       if ( edge->flags & AF_EDGE_DONE )
   1799         continue;
   1800 
   1801       /* skip all non-stem edges */
   1802       edge2 = edge->link;
   1803       if ( !edge2 )
   1804       {
   1805         skipped++;
   1806         continue;
   1807       }
   1808 
   1809       /* Some CJK characters have so many stems that
   1810        * the hinter is likely to merge two adjacent ones.
   1811        * To solve this problem, if either edge of a stem
   1812        * is too close to the previous one, we avoid
   1813        * aligning the two edges, but rather interpolate
   1814        * their locations at the end of this function in
   1815        * order to preserve the space between the stems.
   1816        */
   1817       if ( has_last_stem                       &&
   1818            ( edge->pos  < last_stem_pos + 64 ||
   1819              edge2->pos < last_stem_pos + 64 ) )
   1820       {
   1821         skipped++;
   1822         continue;
   1823       }
   1824 
   1825       /* now align the stem */
   1826 
   1827       /* this should not happen, but it's better to be safe */
   1828       if ( edge2->blue_edge )
   1829       {
   1830         FT_TRACE5(( "ASSERTION FAILED for edge %d\n", edge2-edges ));
   1831 
   1832         af_cjk_align_linked_edge( hints, dim, edge2, edge );
   1833         edge->flags |= AF_EDGE_DONE;
   1834 
   1835 #ifdef FT_DEBUG_LEVEL_TRACE
   1836         num_actions++;
   1837 #endif
   1838 
   1839         continue;
   1840       }
   1841 
   1842       if ( edge2 < edge )
   1843       {
   1844         af_cjk_align_linked_edge( hints, dim, edge2, edge );
   1845         edge->flags |= AF_EDGE_DONE;
   1846 
   1847 #ifdef FT_DEBUG_LEVEL_TRACE
   1848         num_actions++;
   1849 #endif
   1850 
   1851         /* We rarely reaches here it seems;
   1852          * usually the two edges belonging
   1853          * to one stem are marked as DONE together
   1854          */
   1855         has_last_stem = TRUE;
   1856         last_stem_pos = edge->pos;
   1857         continue;
   1858       }
   1859 
   1860       if ( dim != AF_DIMENSION_VERT && !anchor )
   1861       {
   1862 
   1863 #if 0
   1864         if ( fixedpitch )
   1865         {
   1866           AF_Edge     left  = edge;
   1867           AF_Edge     right = edge_limit - 1;
   1868           AF_EdgeRec  left1, left2, right1, right2;
   1869           FT_Pos      target, center1, center2;
   1870           FT_Pos      delta1, delta2, d1, d2;
   1871 
   1872 
   1873           while ( right > left && !right->link )
   1874             right--;
   1875 
   1876           left1  = *left;
   1877           left2  = *left->link;
   1878           right1 = *right->link;
   1879           right2 = *right;
   1880 
   1881           delta  = ( ( ( hinter->pp2.x + 32 ) & -64 ) - hinter->pp2.x ) / 2;
   1882           target = left->opos + ( right->opos - left->opos ) / 2 + delta - 16;
   1883 
   1884           delta1  = delta;
   1885           delta1 += af_hint_normal_stem( hints, left, left->link,
   1886                                          delta1, 0 );
   1887 
   1888           if ( left->link != right )
   1889             af_hint_normal_stem( hints, right->link, right, delta1, 0 );
   1890 
   1891           center1 = left->pos + ( right->pos - left->pos ) / 2;
   1892 
   1893           if ( center1 >= target )
   1894             delta2 = delta - 32;
   1895           else
   1896             delta2 = delta + 32;
   1897 
   1898           delta2 += af_hint_normal_stem( hints, &left1, &left2, delta2, 0 );
   1899 
   1900           if ( delta1 != delta2 )
   1901           {
   1902             if ( left->link != right )
   1903               af_hint_normal_stem( hints, &right1, &right2, delta2, 0 );
   1904 
   1905             center2 = left1.pos + ( right2.pos - left1.pos ) / 2;
   1906 
   1907             d1 = center1 - target;
   1908             d2 = center2 - target;
   1909 
   1910             if ( FT_ABS( d2 ) < FT_ABS( d1 ) )
   1911             {
   1912               left->pos       = left1.pos;
   1913               left->link->pos = left2.pos;
   1914 
   1915               if ( left->link != right )
   1916               {
   1917                 right->link->pos = right1.pos;
   1918                 right->pos       = right2.pos;
   1919               }
   1920 
   1921               delta1 = delta2;
   1922             }
   1923           }
   1924 
   1925           delta               = delta1;
   1926           right->link->flags |= AF_EDGE_DONE;
   1927           right->flags       |= AF_EDGE_DONE;
   1928         }
   1929         else
   1930 
   1931 #endif /* 0 */
   1932 
   1933           delta = af_hint_normal_stem( hints, edge, edge2, 0,
   1934                                        AF_DIMENSION_HORZ );
   1935       }
   1936       else
   1937         af_hint_normal_stem( hints, edge, edge2, delta, dim );
   1938 
   1939 #if 0
   1940       printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
   1941                edge - edges, edge2 - edges,
   1942                ( edge->pos - edge->opos ) / 64.0,
   1943                ( edge2->pos - edge2->opos ) / 64.0 );
   1944 #endif
   1945 
   1946       anchor = edge;
   1947       edge->flags  |= AF_EDGE_DONE;
   1948       edge2->flags |= AF_EDGE_DONE;
   1949       has_last_stem = TRUE;
   1950       last_stem_pos = edge2->pos;
   1951     }
   1952 
   1953     /* make sure that lowercase m's maintain their symmetry */
   1954 
   1955     /* In general, lowercase m's have six vertical edges if they are sans */
   1956     /* serif, or twelve if they are with serifs.  This implementation is  */
   1957     /* based on that assumption, and seems to work very well with most    */
   1958     /* faces.  However, if for a certain face this assumption is not      */
   1959     /* true, the m is just rendered like before.  In addition, any stem   */
   1960     /* correction will only be applied to symmetrical glyphs (even if the */
   1961     /* glyph is not an m), so the potential for unwanted distortion is    */
   1962     /* relatively low.                                                    */
   1963 
   1964     /* We don't handle horizontal edges since we can't easily assure that */
   1965     /* the third (lowest) stem aligns with the base line; it might end up */
   1966     /* one pixel higher or lower.                                         */
   1967 
   1968     n_edges = edge_limit - edges;
   1969     if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
   1970     {
   1971       AF_Edge  edge1, edge2, edge3;
   1972       FT_Pos   dist1, dist2, span;
   1973 
   1974 
   1975       if ( n_edges == 6 )
   1976       {
   1977         edge1 = edges;
   1978         edge2 = edges + 2;
   1979         edge3 = edges + 4;
   1980       }
   1981       else
   1982       {
   1983         edge1 = edges + 1;
   1984         edge2 = edges + 5;
   1985         edge3 = edges + 9;
   1986       }
   1987 
   1988       dist1 = edge2->opos - edge1->opos;
   1989       dist2 = edge3->opos - edge2->opos;
   1990 
   1991       span = dist1 - dist2;
   1992       if ( span < 0 )
   1993         span = -span;
   1994 
   1995       if ( edge1->link == edge1 + 1 &&
   1996            edge2->link == edge2 + 1 &&
   1997            edge3->link == edge3 + 1 && span < 8 )
   1998       {
   1999         delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
   2000         edge3->pos -= delta;
   2001         if ( edge3->link )
   2002           edge3->link->pos -= delta;
   2003 
   2004         /* move the serifs along with the stem */
   2005         if ( n_edges == 12 )
   2006         {
   2007           ( edges + 8 )->pos -= delta;
   2008           ( edges + 11 )->pos -= delta;
   2009         }
   2010 
   2011         edge3->flags |= AF_EDGE_DONE;
   2012         if ( edge3->link )
   2013           edge3->link->flags |= AF_EDGE_DONE;
   2014       }
   2015     }
   2016 
   2017     if ( !skipped )
   2018       goto Exit;
   2019 
   2020     /*
   2021      *  now hint the remaining edges (serifs and single) in order
   2022      *  to complete our processing
   2023      */
   2024     for ( edge = edges; edge < edge_limit; edge++ )
   2025     {
   2026       if ( edge->flags & AF_EDGE_DONE )
   2027         continue;
   2028 
   2029       if ( edge->serif )
   2030       {
   2031         af_cjk_align_serif_edge( hints, edge->serif, edge );
   2032         edge->flags |= AF_EDGE_DONE;
   2033         skipped--;
   2034       }
   2035     }
   2036 
   2037     if ( !skipped )
   2038       goto Exit;
   2039 
   2040     for ( edge = edges; edge < edge_limit; edge++ )
   2041     {
   2042       AF_Edge  before, after;
   2043 
   2044 
   2045       if ( edge->flags & AF_EDGE_DONE )
   2046         continue;
   2047 
   2048       before = after = edge;
   2049 
   2050       while ( --before >= edges )
   2051         if ( before->flags & AF_EDGE_DONE )
   2052           break;
   2053 
   2054       while ( ++after < edge_limit )
   2055         if ( after->flags & AF_EDGE_DONE )
   2056           break;
   2057 
   2058       if ( before >= edges || after < edge_limit )
   2059       {
   2060         if ( before < edges )
   2061           af_cjk_align_serif_edge( hints, after, edge );
   2062         else if ( after >= edge_limit )
   2063           af_cjk_align_serif_edge( hints, before, edge );
   2064         else
   2065         {
   2066           if ( after->fpos == before->fpos )
   2067             edge->pos = before->pos;
   2068           else
   2069             edge->pos = before->pos +
   2070                         FT_MulDiv( edge->fpos - before->fpos,
   2071                                    after->pos - before->pos,
   2072                                    after->fpos - before->fpos );
   2073         }
   2074       }
   2075     }
   2076 
   2077   Exit:
   2078 
   2079 #ifdef FT_DEBUG_LEVEL_TRACE
   2080     if ( !num_actions )
   2081       FT_TRACE5(( "  (none)\n" ));
   2082     FT_TRACE5(( "\n" ));
   2083 #endif
   2084 
   2085     return;
   2086   }
   2087 
   2088 
   2089   static void
   2090   af_cjk_align_edge_points( AF_GlyphHints  hints,
   2091                             AF_Dimension   dim )
   2092   {
   2093     AF_AxisHints  axis       = & hints->axis[dim];
   2094     AF_Edge       edges      = axis->edges;
   2095     AF_Edge       edge_limit = edges + axis->num_edges;
   2096     AF_Edge       edge;
   2097     FT_Bool       snapping;
   2098 
   2099 
   2100     snapping = FT_BOOL( ( dim == AF_DIMENSION_HORZ             &&
   2101                           AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) )  ||
   2102                         ( dim == AF_DIMENSION_VERT             &&
   2103                           AF_LATIN_HINTS_DO_VERT_SNAP( hints ) )  );
   2104 
   2105     for ( edge = edges; edge < edge_limit; edge++ )
   2106     {
   2107       /* move the points of each segment     */
   2108       /* in each edge to the edge's position */
   2109       AF_Segment  seg = edge->first;
   2110 
   2111 
   2112       if ( snapping )
   2113       {
   2114         do
   2115         {
   2116           AF_Point  point = seg->first;
   2117 
   2118 
   2119           for (;;)
   2120           {
   2121             if ( dim == AF_DIMENSION_HORZ )
   2122             {
   2123               point->x      = edge->pos;
   2124               point->flags |= AF_FLAG_TOUCH_X;
   2125             }
   2126             else
   2127             {
   2128               point->y      = edge->pos;
   2129               point->flags |= AF_FLAG_TOUCH_Y;
   2130             }
   2131 
   2132             if ( point == seg->last )
   2133               break;
   2134 
   2135             point = point->next;
   2136           }
   2137 
   2138           seg = seg->edge_next;
   2139 
   2140         } while ( seg != edge->first );
   2141       }
   2142       else
   2143       {
   2144         FT_Pos  delta = edge->pos - edge->opos;
   2145 
   2146 
   2147         do
   2148         {
   2149           AF_Point  point = seg->first;
   2150 
   2151 
   2152           for (;;)
   2153           {
   2154             if ( dim == AF_DIMENSION_HORZ )
   2155             {
   2156               point->x     += delta;
   2157               point->flags |= AF_FLAG_TOUCH_X;
   2158             }
   2159             else
   2160             {
   2161               point->y     += delta;
   2162               point->flags |= AF_FLAG_TOUCH_Y;
   2163             }
   2164 
   2165             if ( point == seg->last )
   2166               break;
   2167 
   2168             point = point->next;
   2169           }
   2170 
   2171           seg = seg->edge_next;
   2172 
   2173         } while ( seg != edge->first );
   2174       }
   2175     }
   2176   }
   2177 
   2178 
   2179   /* Apply the complete hinting algorithm to a CJK glyph. */
   2180 
   2181   FT_LOCAL_DEF( FT_Error )
   2182   af_cjk_hints_apply( AF_GlyphHints  hints,
   2183                       FT_Outline*    outline,
   2184                       AF_CJKMetrics  metrics )
   2185   {
   2186     FT_Error  error;
   2187     int       dim;
   2188 
   2189     FT_UNUSED( metrics );
   2190 
   2191 
   2192     error = af_glyph_hints_reload( hints, outline );
   2193     if ( error )
   2194       goto Exit;
   2195 
   2196     /* analyze glyph outline */
   2197     if ( AF_HINTS_DO_HORIZONTAL( hints ) )
   2198     {
   2199       error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ );
   2200       if ( error )
   2201         goto Exit;
   2202 
   2203       af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_HORZ );
   2204     }
   2205 
   2206     if ( AF_HINTS_DO_VERTICAL( hints ) )
   2207     {
   2208       error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT );
   2209       if ( error )
   2210         goto Exit;
   2211 
   2212       af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_VERT );
   2213     }
   2214 
   2215     /* grid-fit the outline */
   2216     for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
   2217     {
   2218       if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
   2219            ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) )   )
   2220       {
   2221 
   2222 #ifdef AF_CONFIG_OPTION_USE_WARPER
   2223         if ( dim == AF_DIMENSION_HORZ                                  &&
   2224              metrics->root.scaler.render_mode == FT_RENDER_MODE_NORMAL )
   2225         {
   2226           AF_WarperRec  warper;
   2227           FT_Fixed      scale;
   2228           FT_Pos        delta;
   2229 
   2230 
   2231           af_warper_compute( &warper, hints, (AF_Dimension)dim,
   2232                              &scale, &delta );
   2233           af_glyph_hints_scale_dim( hints, (AF_Dimension)dim,
   2234                                     scale, delta );
   2235           continue;
   2236         }
   2237 #endif /* AF_CONFIG_OPTION_USE_WARPER */
   2238 
   2239         af_cjk_hint_edges( hints, (AF_Dimension)dim );
   2240         af_cjk_align_edge_points( hints, (AF_Dimension)dim );
   2241         af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
   2242         af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
   2243       }
   2244     }
   2245 
   2246 #if 0
   2247     af_glyph_hints_dump_points( hints );
   2248     af_glyph_hints_dump_segments( hints );
   2249     af_glyph_hints_dump_edges( hints );
   2250 #endif
   2251 
   2252     af_glyph_hints_save( hints, outline );
   2253 
   2254   Exit:
   2255     return error;
   2256   }
   2257 
   2258 
   2259   /*************************************************************************/
   2260   /*************************************************************************/
   2261   /*****                                                               *****/
   2262   /*****                C J K   S C R I P T   C L A S S                *****/
   2263   /*****                                                               *****/
   2264   /*************************************************************************/
   2265   /*************************************************************************/
   2266 
   2267 
   2268   AF_DEFINE_WRITING_SYSTEM_CLASS(
   2269     af_cjk_writing_system_class,
   2270 
   2271     AF_WRITING_SYSTEM_CJK,
   2272 
   2273     sizeof ( AF_CJKMetricsRec ),
   2274 
   2275     (AF_WritingSystem_InitMetricsFunc) af_cjk_metrics_init,
   2276     (AF_WritingSystem_ScaleMetricsFunc)af_cjk_metrics_scale,
   2277     (AF_WritingSystem_DoneMetricsFunc) NULL,
   2278 
   2279     (AF_WritingSystem_InitHintsFunc)   af_cjk_hints_init,
   2280     (AF_WritingSystem_ApplyHintsFunc)  af_cjk_hints_apply
   2281   )
   2282 
   2283 
   2284 #else /* !AF_CONFIG_OPTION_CJK */
   2285 
   2286 
   2287   AF_DEFINE_WRITING_SYSTEM_CLASS(
   2288     af_cjk_writing_system_class,
   2289 
   2290     AF_WRITING_SYSTEM_CJK,
   2291 
   2292     sizeof ( AF_CJKMetricsRec ),
   2293 
   2294     (AF_WritingSystem_InitMetricsFunc) NULL,
   2295     (AF_WritingSystem_ScaleMetricsFunc)NULL,
   2296     (AF_WritingSystem_DoneMetricsFunc) NULL,
   2297 
   2298     (AF_WritingSystem_InitHintsFunc)   NULL,
   2299     (AF_WritingSystem_ApplyHintsFunc)  NULL
   2300   )
   2301 
   2302 
   2303 #endif /* !AF_CONFIG_OPTION_CJK */
   2304 
   2305 
   2306 /* END */
   2307