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