(root)/
freetype-2.13.2/
src/
autofit/
afloader.c
       1  /****************************************************************************
       2   *
       3   * afloader.c
       4   *
       5   *   Auto-fitter glyph loading routines (body).
       6   *
       7   * Copyright (C) 2003-2023 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 */