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