tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

afloader.c (24004B)


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