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