"Fossies" - the Fresh Open Source Software Archive

Member "SDL2_ttf-2.20.2/external/freetype/src/autofit/afloader.c" (25 May 2022, 24105 Bytes) of package /linux/misc/SDL2_ttf-2.20.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "afloader.c" see the Fossies "Dox" file reference documentation.

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