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