1 /****************************************************************************
2  *
3  * afloader.c
4  *
5  *   Auto-fitter glyph loading routines (body).
6  *
7  * Copyright (C) 2003-2020 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     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       {
425         error = writing_system_class->style_hints_apply(
426                   glyph_index,
427                   hints,
428                   &gloader->base.outline,
429                   style_metrics );
430         if ( error )
431           goto Exit;
432       }
433 
434       /* we now need to adjust the metrics according to the change in */
435       /* width/positioning that occurred during the hinting process   */
436       if ( scaler.render_mode != FT_RENDER_MODE_LIGHT )
437       {
438         AF_AxisHints  axis  = &hints->axis[AF_DIMENSION_HORZ];
439 
440 
441         if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) )
442         {
443           AF_Edge  edge1 = axis->edges;         /* leftmost edge  */
444           AF_Edge  edge2 = edge1 +
445                            axis->num_edges - 1; /* rightmost edge */
446 
447           FT_Pos  old_rsb = loader->pp2.x - edge2->opos;
448           /* loader->pp1.x is always zero at this point of time */
449           FT_Pos  old_lsb = edge1->opos;     /* - loader->pp1.x */
450           FT_Pos  new_lsb = edge1->pos;
451 
452           /* remember unhinted values to later account */
453           /* for rounding errors                       */
454           FT_Pos  pp1x_uh = new_lsb    - old_lsb;
455           FT_Pos  pp2x_uh = edge2->pos + old_rsb;
456 
457 
458           /* prefer too much space over too little space */
459           /* for very small sizes                        */
460 
461           if ( old_lsb < 24 )
462             pp1x_uh -= 8;
463 
464           if ( old_rsb < 24 )
465             pp2x_uh += 8;
466 
467           loader->pp1.x = FT_PIX_ROUND( pp1x_uh );
468           loader->pp2.x = FT_PIX_ROUND( pp2x_uh );
469 
470           if ( loader->pp1.x >= new_lsb && old_lsb > 0 )
471             loader->pp1.x -= 64;
472 
473           if ( loader->pp2.x <= edge2->pos && old_rsb > 0 )
474             loader->pp2.x += 64;
475 
476           slot->lsb_delta = loader->pp1.x - pp1x_uh;
477           slot->rsb_delta = loader->pp2.x - pp2x_uh;
478         }
479         else
480         {
481           FT_Pos  pp1x = loader->pp1.x;
482           FT_Pos  pp2x = loader->pp2.x;
483 
484 
485           loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
486           loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );
487 
488           slot->lsb_delta = loader->pp1.x - pp1x;
489           slot->rsb_delta = loader->pp2.x - pp2x;
490         }
491       }
492       /* `light' mode uses integer advance widths */
493       /* but sets `lsb_delta' and `rsb_delta'     */
494       else
495       {
496         FT_Pos  pp1x = loader->pp1.x;
497         FT_Pos  pp2x = loader->pp2.x;
498 
499 
500         loader->pp1.x = FT_PIX_ROUND( pp1x );
501         loader->pp2.x = FT_PIX_ROUND( pp2x );
502 
503         slot->lsb_delta = loader->pp1.x - pp1x;
504         slot->rsb_delta = loader->pp2.x - pp2x;
505       }
506 
507       break;
508 
509     default:
510       /* we don't support other formats (yet?) */
511       error = FT_THROW( Unimplemented_Feature );
512     }
513 
514   Hint_Metrics:
515     {
516       FT_BBox    bbox;
517       FT_Vector  vvector;
518 
519 
520       vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
521       vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
522       vvector.x = FT_MulFix( vvector.x, style_metrics->scaler.x_scale );
523       vvector.y = FT_MulFix( vvector.y, style_metrics->scaler.y_scale );
524 
525       /* transform the hinted outline if needed */
526       if ( loader->transformed )
527       {
528         FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix );
529         FT_Vector_Transform( &vvector, &loader->trans_matrix );
530       }
531 
532       /* we must translate our final outline by -pp1.x and compute */
533       /* the new metrics                                           */
534       if ( loader->pp1.x )
535         FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 );
536 
537       FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
538 
539       bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
540       bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
541       bbox.xMax = FT_PIX_CEIL(  bbox.xMax );
542       bbox.yMax = FT_PIX_CEIL(  bbox.yMax );
543 
544       slot->metrics.width        = bbox.xMax - bbox.xMin;
545       slot->metrics.height       = bbox.yMax - bbox.yMin;
546       slot->metrics.horiBearingX = bbox.xMin;
547       slot->metrics.horiBearingY = bbox.yMax;
548 
549       slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x );
550       slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y );
551 
552       /* for mono-width fonts (like Andale, Courier, etc.) we need */
553       /* to keep the original rounded advance width; ditto for     */
554       /* digits if all have the same advance width                 */
555       if ( scaler.render_mode != FT_RENDER_MODE_LIGHT                       &&
556            ( FT_IS_FIXED_WIDTH( slot->face )                              ||
557              ( af_face_globals_is_digit( loader->globals, glyph_index ) &&
558                style_metrics->digits_have_same_width                    ) ) )
559       {
560         slot->metrics.horiAdvance =
561           FT_MulFix( slot->metrics.horiAdvance,
562                      style_metrics->scaler.x_scale );
563 
564         /* Set delta values to 0.  Otherwise code that uses them is */
565         /* going to ruin the fixed advance width.                   */
566         slot->lsb_delta = 0;
567         slot->rsb_delta = 0;
568       }
569       else
570       {
571         /* non-spacing glyphs must stay as-is */
572         if ( slot->metrics.horiAdvance )
573           slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
574       }
575 
576       slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
577                                              style_metrics->scaler.y_scale );
578 
579       slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
580       slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );
581 
582       slot->format  = FT_GLYPH_FORMAT_OUTLINE;
583     }
584 
585   Exit:
586     return error;
587   }
588 
589 
590   /*
591    * Compute amount of font units the face should be emboldened by, in
592    * analogy to the CFF driver's `cf2_computeDarkening' function.  See there
593    * for details of the algorithm.
594    *
595    * XXX: Currently a crude adaption of the original algorithm.  Do better?
596    */
597   FT_LOCAL_DEF( FT_Int32 )
af_loader_compute_darkening(AF_Loader loader,FT_Face face,FT_Pos standard_width)598   af_loader_compute_darkening( AF_Loader  loader,
599                                FT_Face    face,
600                                FT_Pos     standard_width )
601   {
602     AF_Module  module = loader->globals->module;
603 
604     FT_UShort  units_per_EM;
605     FT_Fixed   ppem, em_ratio;
606     FT_Fixed   stem_width, stem_width_per_1000, scaled_stem, darken_amount;
607     FT_Int     log_base_2;
608     FT_Int     x1, y1, x2, y2, x3, y3, x4, y4;
609 
610 
611     ppem         = FT_MAX( af_intToFixed( 4 ),
612                            af_intToFixed( face->size->metrics.x_ppem ) );
613     units_per_EM = face->units_per_EM;
614 
615     em_ratio = FT_DivFix( af_intToFixed( 1000 ),
616                           af_intToFixed ( units_per_EM ) );
617     if ( em_ratio < af_floatToFixed( .01 ) )
618     {
619       /* If something goes wrong, don't embolden. */
620       return 0;
621     }
622 
623     x1 = module->darken_params[0];
624     y1 = module->darken_params[1];
625     x2 = module->darken_params[2];
626     y2 = module->darken_params[3];
627     x3 = module->darken_params[4];
628     y3 = module->darken_params[5];
629     x4 = module->darken_params[6];
630     y4 = module->darken_params[7];
631 
632     if ( standard_width <= 0 )
633     {
634       stem_width          = af_intToFixed( 75 ); /* taken from cf2font.c */
635       stem_width_per_1000 = stem_width;
636     }
637     else
638     {
639       stem_width          = af_intToFixed( standard_width );
640       stem_width_per_1000 = FT_MulFix( stem_width, em_ratio );
641     }
642 
643     log_base_2 = FT_MSB( (FT_UInt32)stem_width_per_1000 ) +
644                  FT_MSB( (FT_UInt32)ppem );
645 
646     if ( log_base_2 >= 46 )
647     {
648       /* possible overflow */
649       scaled_stem = af_intToFixed( x4 );
650     }
651     else
652       scaled_stem = FT_MulFix( stem_width_per_1000, ppem );
653 
654     /* now apply the darkening parameters */
655     if ( scaled_stem < af_intToFixed( x1 ) )
656       darken_amount = FT_DivFix( af_intToFixed( y1 ), ppem );
657 
658     else if ( scaled_stem < af_intToFixed( x2 ) )
659     {
660       FT_Int  xdelta = x2 - x1;
661       FT_Int  ydelta = y2 - y1;
662       FT_Int  x      = stem_width_per_1000 -
663                        FT_DivFix( af_intToFixed( x1 ), ppem );
664 
665 
666       if ( !xdelta )
667         goto Try_x3;
668 
669       darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
670                       FT_DivFix( af_intToFixed( y1 ), ppem );
671     }
672 
673     else if ( scaled_stem < af_intToFixed( x3 ) )
674     {
675     Try_x3:
676       {
677         FT_Int  xdelta = x3 - x2;
678         FT_Int  ydelta = y3 - y2;
679         FT_Int  x      = stem_width_per_1000 -
680                          FT_DivFix( af_intToFixed( x2 ), ppem );
681 
682 
683         if ( !xdelta )
684           goto Try_x4;
685 
686         darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
687                         FT_DivFix( af_intToFixed( y2 ), ppem );
688       }
689     }
690 
691     else if ( scaled_stem < af_intToFixed( x4 ) )
692     {
693     Try_x4:
694       {
695         FT_Int  xdelta = x4 - x3;
696         FT_Int  ydelta = y4 - y3;
697         FT_Int  x      = stem_width_per_1000 -
698                          FT_DivFix( af_intToFixed( x3 ), ppem );
699 
700 
701         if ( !xdelta )
702           goto Use_y4;
703 
704         darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
705                         FT_DivFix( af_intToFixed( y3 ), ppem );
706       }
707     }
708 
709     else
710     {
711     Use_y4:
712       darken_amount = FT_DivFix( af_intToFixed( y4 ), ppem );
713     }
714 
715     /* Convert darken_amount from per 1000 em to true character space. */
716     return af_fixedToInt( FT_DivFix( darken_amount, em_ratio ) );
717   }
718 
719 
720 /* END */
721