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