xref: /aosp_15_r20/external/freetype/src/autofit/afloader.c (revision 63949dbd25bcc50c4e1178497ff9e9574d44fc5a)
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      = (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 )
af_loader_done(AF_Loader loader)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
af_loader_embolden_glyph_in_slot(AF_Loader loader,FT_Face face,AF_StyleMetrics style_metrics)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 )
af_loader_load_glyph(AF_Loader loader,AF_Module module,FT_Face face,FT_UInt glyph_index,FT_Int32 load_flags)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 )
af_loader_compute_darkening(AF_Loader loader,FT_Face face,FT_Pos standard_width)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 */
707