Home | History | Annotate | Download | only in autofit
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  afloader.c                                                             */
      4 /*                                                                         */
      5 /*    Auto-fitter glyph loading routines (body).                           */
      6 /*                                                                         */
      7 /*  Copyright 2003-2016 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 #include "afglobal.h"
     20 #include "afloader.h"
     21 #include "afhints.h"
     22 #include "aferrors.h"
     23 #include "afmodule.h"
     24 #include "afpic.h"
     25 
     26 #include FT_INTERNAL_CALC_H
     27 
     28 
     29   /* Initialize glyph loader. */
     30 
     31   FT_LOCAL_DEF( void )
     32   af_loader_init( AF_Loader      loader,
     33                   AF_GlyphHints  hints )
     34   {
     35     FT_ZERO( loader );
     36 
     37     loader->hints = hints;
     38   }
     39 
     40 
     41   /* Reset glyph loader and compute globals if necessary. */
     42 
     43   FT_LOCAL_DEF( FT_Error )
     44   af_loader_reset( AF_Loader  loader,
     45                    AF_Module  module,
     46                    FT_Face    face )
     47   {
     48     FT_Error  error = FT_Err_Ok;
     49 
     50 
     51     loader->face    = face;
     52     loader->globals = (AF_FaceGlobals)face->autohint.data;
     53 
     54     if ( loader->globals == NULL )
     55     {
     56       error = af_face_globals_new( face, &loader->globals, module );
     57       if ( !error )
     58       {
     59         face->autohint.data =
     60           (FT_Pointer)loader->globals;
     61         face->autohint.finalizer =
     62           (FT_Generic_Finalizer)af_face_globals_free;
     63       }
     64     }
     65 
     66     return error;
     67   }
     68 
     69 
     70   /* Finalize glyph loader. */
     71 
     72   FT_LOCAL_DEF( void )
     73   af_loader_done( AF_Loader  loader )
     74   {
     75     loader->face    = NULL;
     76     loader->globals = NULL;
     77     loader->hints   = NULL;
     78   }
     79 
     80 
     81 #define af_intToFixed( i ) \
     82           ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
     83 #define af_fixedToInt( x ) \
     84           ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
     85 #define af_floatToFixed( f ) \
     86           ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) )
     87 
     88 
     89   /* Do the main work of `af_loader_load_glyph'.  Note that we never   */
     90   /* have to deal with composite glyphs as those get loaded into       */
     91   /* FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function. */
     92   /* In the rare cases where FT_LOAD_NO_RECURSE is set, it implies     */
     93   /* FT_LOAD_NO_SCALE and as such the auto-hinter is never called.     */
     94 
     95   static FT_Error
     96   af_loader_load_g( AF_Loader  loader,
     97                     AF_Scaler  scaler,
     98                     FT_UInt    glyph_index,
     99                     FT_Int32   load_flags )
    100   {
    101     AF_Module  module = loader->globals->module;
    102 
    103     FT_Error          error;
    104     FT_Face           face     = loader->face;
    105     AF_StyleMetrics   metrics  = loader->metrics;
    106     AF_GlyphHints     hints    = loader->hints;
    107     FT_GlyphSlot      slot     = face->glyph;
    108     FT_Slot_Internal  internal = slot->internal;
    109     FT_GlyphLoader    gloader  = internal->loader;
    110     FT_Int32          flags;
    111 
    112 
    113     flags = load_flags | FT_LOAD_LINEAR_DESIGN;
    114     error = FT_Load_Glyph( face, glyph_index, flags );
    115     if ( error )
    116       goto Exit;
    117 
    118     /*
    119      * Apply stem darkening (emboldening) here before hints are applied to
    120      * the outline.  Glyphs are scaled down proportionally to the
    121      * emboldening so that curve points don't fall outside their precomputed
    122      * blue zones.
    123      *
    124      * Any emboldening done by the font driver (e.g., the CFF driver)
    125      * doesn't reach here because the autohinter loads the unprocessed
    126      * glyphs in font units for analysis (functions `af_*_metrics_init_*')
    127      * and then above to prepare it for the rasterizers by itself,
    128      * independently of the font driver.  So emboldening must be done here,
    129      * within the autohinter.
    130      *
    131      * All glyphs to be autohinted pass through here one by one.  The
    132      * standard widths can therefore change from one glyph to the next,
    133      * depending on what script a glyph is assigned to (each script has its
    134      * own set of standard widths and other metrics).  The darkening amount
    135      * must therefore be recomputed for each size and
    136      * `standard_{vertical,horizontal}_width' change.
    137      */
    138     if ( !module->no_stem_darkening )
    139     {
    140       AF_FaceGlobals         globals = loader->globals;
    141       AF_WritingSystemClass  writing_system_class;
    142 
    143       FT_Pos  stdVW = 0;
    144       FT_Pos  stdHW = 0;
    145 
    146       FT_Bool  size_changed = face->size->metrics.x_ppem
    147                                 != globals->stem_darkening_for_ppem;
    148 
    149       FT_Fixed  em_size  = af_intToFixed( face->units_per_EM );
    150       FT_Fixed  em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size );
    151 
    152       FT_Matrix  scale_down_matrix = { 0x10000L, 0, 0, 0x10000L };
    153 
    154 
    155       /* Skip stem darkening for broken fonts. */
    156       if ( !face->units_per_EM )
    157         goto After_Emboldening;
    158 
    159       /*
    160        * We depend on the writing system (script analyzers) to supply
    161        * standard widths for the script of the glyph we are looking at.  If
    162        * it can't deliver, stem darkening is effectively disabled.
    163        */
    164       writing_system_class =
    165         AF_WRITING_SYSTEM_CLASSES_GET[metrics->style_class->writing_system];
    166 
    167       if ( writing_system_class->style_metrics_getstdw )
    168         writing_system_class->style_metrics_getstdw( metrics,
    169                                                      &stdHW,
    170                                                      &stdVW );
    171       else
    172         goto After_Emboldening;
    173 
    174 
    175       if ( size_changed                                               ||
    176            ( stdVW > 0 && stdVW != globals->standard_vertical_width ) )
    177       {
    178         FT_Fixed  darken_by_font_units_x, darken_x;
    179 
    180 
    181         darken_by_font_units_x =
    182           af_intToFixed( af_loader_compute_darkening( loader,
    183                                                       face,
    184                                                       stdVW ) );
    185         darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x,
    186                                          face->size->metrics.x_scale ),
    187                               em_ratio );
    188 
    189         globals->standard_vertical_width = stdVW;
    190         globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
    191         globals->darken_x                = af_fixedToInt( darken_x );
    192       }
    193 
    194       if ( size_changed                                                 ||
    195            ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) )
    196       {
    197         FT_Fixed  darken_by_font_units_y, darken_y;
    198 
    199 
    200         darken_by_font_units_y =
    201           af_intToFixed( af_loader_compute_darkening( loader,
    202                                                       face,
    203                                                       stdHW ) );
    204         darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y,
    205                                          face->size->metrics.y_scale ),
    206                               em_ratio );
    207 
    208         globals->standard_horizontal_width = stdHW;
    209         globals->stem_darkening_for_ppem   = face->size->metrics.x_ppem;
    210         globals->darken_y                  = af_fixedToInt( darken_y );
    211 
    212         /*
    213          * Scale outlines down on the Y-axis to keep them inside their blue
    214          * zones.  The stronger the emboldening, the stronger the
    215          * downscaling (plus heuristical padding to prevent outlines still
    216          * falling out their zones due to rounding).
    217          *
    218          * Reason: `FT_Outline_Embolden' works by shifting the rightmost
    219          * points of stems farther to the right, and topmost points farther
    220          * up.  This positions points on the Y-axis outside their
    221          * pre-computed blue zones and leads to distortion when applying the
    222          * hints in the code further below.  Code outside this emboldening
    223          * block doesn't know we are presenting it with modified outlines
    224          * the analyzer didn't see!
    225          *
    226          * An unfortunate side effect of downscaling is that the emboldening
    227          * effect is slightly decreased.  The loss becomes more pronounced
    228          * versus the CFF driver at smaller sizes, e.g., at 9ppem and below.
    229          */
    230         globals->scale_down_factor =
    231           FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ),
    232                      em_size );
    233       }
    234 
    235       FT_Outline_EmboldenXY( &slot->outline,
    236                              globals->darken_x,
    237                              globals->darken_y );
    238 
    239       scale_down_matrix.yy = globals->scale_down_factor;
    240       FT_Outline_Transform( &slot->outline, &scale_down_matrix );
    241     }
    242 
    243   After_Emboldening:
    244     loader->transformed = internal->glyph_transformed;
    245     if ( loader->transformed )
    246     {
    247       FT_Matrix  inverse;
    248 
    249 
    250       loader->trans_matrix = internal->glyph_matrix;
    251       loader->trans_delta  = internal->glyph_delta;
    252 
    253       inverse = loader->trans_matrix;
    254       if ( !FT_Matrix_Invert( &inverse ) )
    255         FT_Vector_Transform( &loader->trans_delta, &inverse );
    256     }
    257 
    258     switch ( slot->format )
    259     {
    260     case FT_GLYPH_FORMAT_OUTLINE:
    261       /* translate the loaded glyph when an internal transform is needed */
    262       if ( loader->transformed )
    263         FT_Outline_Translate( &slot->outline,
    264                               loader->trans_delta.x,
    265                               loader->trans_delta.y );
    266 
    267       /* compute original horizontal phantom points (and ignore */
    268       /* vertical ones)                                         */
    269       loader->pp1.x = hints->x_delta;
    270       loader->pp1.y = hints->y_delta;
    271       loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance,
    272                                  hints->x_scale ) + hints->x_delta;
    273       loader->pp2.y = hints->y_delta;
    274 
    275       /* be sure to check for spacing glyphs */
    276       if ( slot->outline.n_points == 0 )
    277         goto Hint_Metrics;
    278 
    279       /* now load the slot image into the auto-outline and run the */
    280       /* automatic hinting process                                 */
    281       {
    282 #ifdef FT_CONFIG_OPTION_PIC
    283         AF_FaceGlobals         globals = loader->globals;
    284 #endif
    285         AF_StyleClass          style_class = metrics->style_class;
    286         AF_WritingSystemClass  writing_system_class =
    287           AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system];
    288 
    289 
    290         if ( writing_system_class->style_hints_apply )
    291           writing_system_class->style_hints_apply( glyph_index,
    292                                                    hints,
    293                                                    &gloader->base.outline,
    294                                                    metrics );
    295       }
    296 
    297       /* we now need to adjust the metrics according to the change in */
    298       /* width/positioning that occurred during the hinting process   */
    299       if ( scaler->render_mode != FT_RENDER_MODE_LIGHT )
    300       {
    301         FT_Pos        old_rsb, old_lsb, new_lsb;
    302         FT_Pos        pp1x_uh, pp2x_uh;
    303         AF_AxisHints  axis  = &hints->axis[AF_DIMENSION_HORZ];
    304         AF_Edge       edge1 = axis->edges;         /* leftmost edge  */
    305         AF_Edge       edge2 = edge1 +
    306                               axis->num_edges - 1; /* rightmost edge */
    307 
    308 
    309         if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) )
    310         {
    311           old_rsb = loader->pp2.x - edge2->opos;
    312           old_lsb = edge1->opos;
    313           new_lsb = edge1->pos;
    314 
    315           /* remember unhinted values to later account */
    316           /* for rounding errors                       */
    317 
    318           pp1x_uh = new_lsb    - old_lsb;
    319           pp2x_uh = edge2->pos + old_rsb;
    320 
    321           /* prefer too much space over too little space */
    322           /* for very small sizes                        */
    323 
    324           if ( old_lsb < 24 )
    325             pp1x_uh -= 8;
    326 
    327           if ( old_rsb < 24 )
    328             pp2x_uh += 8;
    329 
    330           loader->pp1.x = FT_PIX_ROUND( pp1x_uh );
    331           loader->pp2.x = FT_PIX_ROUND( pp2x_uh );
    332 
    333           if ( loader->pp1.x >= new_lsb && old_lsb > 0 )
    334             loader->pp1.x -= 64;
    335 
    336           if ( loader->pp2.x <= edge2->pos && old_rsb > 0 )
    337             loader->pp2.x += 64;
    338 
    339           slot->lsb_delta = loader->pp1.x - pp1x_uh;
    340           slot->rsb_delta = loader->pp2.x - pp2x_uh;
    341         }
    342         else
    343         {
    344           FT_Pos  pp1x = loader->pp1.x;
    345           FT_Pos  pp2x = loader->pp2.x;
    346 
    347 
    348           loader->pp1.x = FT_PIX_ROUND( pp1x );
    349           loader->pp2.x = FT_PIX_ROUND( pp2x );
    350 
    351           slot->lsb_delta = loader->pp1.x - pp1x;
    352           slot->rsb_delta = loader->pp2.x - pp2x;
    353         }
    354       }
    355       else
    356       {
    357         FT_Pos  pp1x = loader->pp1.x;
    358         FT_Pos  pp2x = loader->pp2.x;
    359 
    360 
    361         loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
    362         loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );
    363 
    364         slot->lsb_delta = loader->pp1.x - pp1x;
    365         slot->rsb_delta = loader->pp2.x - pp2x;
    366       }
    367 
    368       break;
    369 
    370     default:
    371       /* we don't support other formats (yet?) */
    372       error = FT_THROW( Unimplemented_Feature );
    373     }
    374 
    375   Hint_Metrics:
    376     {
    377       FT_BBox    bbox;
    378       FT_Vector  vvector;
    379 
    380 
    381       vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
    382       vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
    383       vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale );
    384       vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale );
    385 
    386       /* transform the hinted outline if needed */
    387       if ( loader->transformed )
    388       {
    389         FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix );
    390         FT_Vector_Transform( &vvector, &loader->trans_matrix );
    391       }
    392 #if 1
    393       /* we must translate our final outline by -pp1.x and compute */
    394       /* the new metrics                                           */
    395       if ( loader->pp1.x )
    396         FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 );
    397 #endif
    398       FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
    399 
    400       bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
    401       bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
    402       bbox.xMax = FT_PIX_CEIL(  bbox.xMax );
    403       bbox.yMax = FT_PIX_CEIL(  bbox.yMax );
    404 
    405       slot->metrics.width        = bbox.xMax - bbox.xMin;
    406       slot->metrics.height       = bbox.yMax - bbox.yMin;
    407       slot->metrics.horiBearingX = bbox.xMin;
    408       slot->metrics.horiBearingY = bbox.yMax;
    409 
    410       slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x );
    411       slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y );
    412 
    413       /* for mono-width fonts (like Andale, Courier, etc.) we need */
    414       /* to keep the original rounded advance width; ditto for     */
    415       /* digits if all have the same advance width                 */
    416 #if 0
    417       if ( !FT_IS_FIXED_WIDTH( slot->face ) )
    418         slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
    419       else
    420         slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
    421                                                x_scale );
    422 #else
    423       if ( scaler->render_mode != FT_RENDER_MODE_LIGHT                      &&
    424            ( FT_IS_FIXED_WIDTH( slot->face )                              ||
    425              ( af_face_globals_is_digit( loader->globals, glyph_index ) &&
    426                metrics->digits_have_same_width                          ) ) )
    427       {
    428         slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
    429                                                metrics->scaler.x_scale );
    430 
    431         /* Set delta values to 0.  Otherwise code that uses them is */
    432         /* going to ruin the fixed advance width.                   */
    433         slot->lsb_delta = 0;
    434         slot->rsb_delta = 0;
    435       }
    436       else
    437       {
    438         /* non-spacing glyphs must stay as-is */
    439         if ( slot->metrics.horiAdvance )
    440           slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
    441       }
    442 #endif
    443 
    444       slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
    445                                              metrics->scaler.y_scale );
    446 
    447       slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
    448       slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );
    449 
    450 #if 0
    451       /* reassign all outline fields except flags to protect them */
    452       slot->outline.n_contours = internal->loader->base.outline.n_contours;
    453       slot->outline.n_points   = internal->loader->base.outline.n_points;
    454       slot->outline.points     = internal->loader->base.outline.points;
    455       slot->outline.tags       = internal->loader->base.outline.tags;
    456       slot->outline.contours   = internal->loader->base.outline.contours;
    457 #endif
    458 
    459       slot->format  = FT_GLYPH_FORMAT_OUTLINE;
    460     }
    461 
    462   Exit:
    463     return error;
    464   }
    465 
    466 
    467   /* Load a glyph. */
    468 
    469   FT_LOCAL_DEF( FT_Error )
    470   af_loader_load_glyph( AF_Loader  loader,
    471                         AF_Module  module,
    472                         FT_Face    face,
    473                         FT_UInt    gindex,
    474                         FT_Int32   load_flags )
    475   {
    476     FT_Error      error;
    477     FT_Size       size   = face->size;
    478     AF_ScalerRec  scaler;
    479 
    480 
    481     if ( !size )
    482       return FT_THROW( Invalid_Size_Handle );
    483 
    484     FT_ZERO( &scaler );
    485 
    486     scaler.face    = face;
    487     scaler.x_scale = size->metrics.x_scale;
    488     scaler.x_delta = 0;  /* XXX: TODO: add support for sub-pixel hinting */
    489     scaler.y_scale = size->metrics.y_scale;
    490     scaler.y_delta = 0;  /* XXX: TODO: add support for sub-pixel hinting */
    491 
    492     scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags );
    493     scaler.flags       = 0;  /* XXX: fix this */
    494 
    495     error = af_loader_reset( loader, module, face );
    496     if ( !error )
    497     {
    498       AF_StyleMetrics  metrics;
    499       FT_UInt          options = AF_STYLE_NONE_DFLT;
    500 
    501 
    502 #ifdef FT_OPTION_AUTOFIT2
    503       /* XXX: undocumented hook to activate the latin2 writing system */
    504       if ( load_flags & ( 1UL << 20 ) )
    505         options = AF_STYLE_LTN2_DFLT;
    506 #endif
    507 
    508       error = af_face_globals_get_metrics( loader->globals, gindex,
    509                                            options, &metrics );
    510       if ( !error )
    511       {
    512 #ifdef FT_CONFIG_OPTION_PIC
    513         AF_FaceGlobals         globals = loader->globals;
    514 #endif
    515         AF_StyleClass          style_class = metrics->style_class;
    516         AF_WritingSystemClass  writing_system_class =
    517           AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system];
    518 
    519 
    520         loader->metrics = metrics;
    521 
    522         if ( writing_system_class->style_metrics_scale )
    523           writing_system_class->style_metrics_scale( metrics, &scaler );
    524         else
    525           metrics->scaler = scaler;
    526 
    527         load_flags |=  FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
    528         load_flags &= ~FT_LOAD_RENDER;
    529 
    530         if ( writing_system_class->style_hints_init )
    531         {
    532           error = writing_system_class->style_hints_init( loader->hints,
    533                                                           metrics );
    534           if ( error )
    535             goto Exit;
    536         }
    537 
    538         error = af_loader_load_g( loader, &scaler, gindex, load_flags );
    539       }
    540     }
    541   Exit:
    542     return error;
    543   }
    544 
    545 
    546   /*
    547    * Compute amount of font units the face should be emboldened by, in
    548    * analogy to the CFF driver's `cf2_computeDarkening' function.  See there
    549    * for details of the algorithm.
    550    *
    551    * XXX: Currently a crude adaption of the original algorithm.  Do better?
    552    */
    553   FT_LOCAL_DEF( FT_Int32 )
    554   af_loader_compute_darkening( AF_Loader  loader,
    555                                FT_Face    face,
    556                                FT_Pos     standard_width )
    557   {
    558     AF_Module  module = loader->globals->module;
    559 
    560     FT_UShort  units_per_EM;
    561     FT_Fixed   ppem, em_ratio;
    562     FT_Fixed   stem_width, stem_width_per_1000, scaled_stem, darken_amount;
    563     FT_Int     log_base_2;
    564     FT_Int     x1, y1, x2, y2, x3, y3, x4, y4;
    565 
    566 
    567     ppem         = FT_MAX( af_intToFixed( 4 ),
    568                            af_intToFixed( face->size->metrics.x_ppem ) );
    569     units_per_EM = face->units_per_EM;
    570 
    571     em_ratio = FT_DivFix( af_intToFixed( 1000 ),
    572                           af_intToFixed ( units_per_EM ) );
    573     if ( em_ratio < af_floatToFixed( .01 ) )
    574     {
    575       /* If something goes wrong, don't embolden. */
    576       return 0;
    577     }
    578 
    579     x1 = module->darken_params[0];
    580     y1 = module->darken_params[1];
    581     x2 = module->darken_params[2];
    582     y2 = module->darken_params[3];
    583     x3 = module->darken_params[4];
    584     y3 = module->darken_params[5];
    585     x4 = module->darken_params[6];
    586     y4 = module->darken_params[7];
    587 
    588     if ( standard_width <= 0 )
    589     {
    590       stem_width          = af_intToFixed( 75 ); /* taken from cf2font.c */
    591       stem_width_per_1000 = stem_width;
    592     }
    593     else
    594     {
    595       stem_width          = af_intToFixed( standard_width );
    596       stem_width_per_1000 = FT_MulFix( stem_width, em_ratio );
    597     }
    598 
    599     log_base_2 = FT_MSB( (FT_UInt32)stem_width_per_1000 ) +
    600                  FT_MSB( (FT_UInt32)ppem );
    601 
    602     if ( log_base_2 >= 46 )
    603     {
    604       /* possible overflow */
    605       scaled_stem = af_intToFixed( x4 );
    606     }
    607     else
    608       scaled_stem = FT_MulFix( stem_width_per_1000, ppem );
    609 
    610     /* now apply the darkening parameters */
    611     if ( scaled_stem < af_intToFixed( x1 ) )
    612       darken_amount = FT_DivFix( af_intToFixed( y1 ), ppem );
    613 
    614     else if ( scaled_stem < af_intToFixed( x2 ) )
    615     {
    616       FT_Int  xdelta = x2 - x1;
    617       FT_Int  ydelta = y2 - y1;
    618       FT_Int  x      = stem_width_per_1000 -
    619                        FT_DivFix( af_intToFixed( x1 ), ppem );
    620 
    621 
    622       if ( !xdelta )
    623         goto Try_x3;
    624 
    625       darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
    626                       FT_DivFix( af_intToFixed( y1 ), ppem );
    627     }
    628 
    629     else if ( scaled_stem < af_intToFixed( x3 ) )
    630     {
    631     Try_x3:
    632       {
    633         FT_Int  xdelta = x3 - x2;
    634         FT_Int  ydelta = y3 - y2;
    635         FT_Int  x      = stem_width_per_1000 -
    636                          FT_DivFix( af_intToFixed( x2 ), ppem );
    637 
    638 
    639         if ( !xdelta )
    640           goto Try_x4;
    641 
    642         darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
    643                         FT_DivFix( af_intToFixed( y2 ), ppem );
    644       }
    645     }
    646 
    647     else if ( scaled_stem < af_intToFixed( x4 ) )
    648     {
    649     Try_x4:
    650       {
    651         FT_Int  xdelta = x4 - x3;
    652         FT_Int  ydelta = y4 - y3;
    653         FT_Int  x      = stem_width_per_1000 -
    654                          FT_DivFix( af_intToFixed( x3 ), ppem );
    655 
    656 
    657         if ( !xdelta )
    658           goto Use_y4;
    659 
    660         darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
    661                         FT_DivFix( af_intToFixed( y3 ), ppem );
    662       }
    663     }
    664 
    665     else
    666     {
    667     Use_y4:
    668       darken_amount = FT_DivFix( af_intToFixed( y4 ), ppem );
    669     }
    670 
    671     /* Convert darken_amount from per 1000 em to true character space. */
    672     return af_fixedToInt( FT_DivFix( darken_amount, em_ratio ) );
    673   }
    674 
    675 
    676 /* END */
    677