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