1 /**************************************************************************** 2 * 3 * pshglob.c 4 * 5 * PostScript hinter global hinting management (body). 6 * Inspired by the new auto-hinter module. 7 * 8 * Copyright (C) 2001-2020 by 9 * David Turner, Robert Wilhelm, and Werner Lemberg. 10 * 11 * This file is part of the FreeType project, and may only be used 12 * modified and distributed under the terms of the FreeType project 13 * license, LICENSE.TXT. By continuing to use, modify, or distribute 14 * this file you indicate that you have read the license and 15 * understand and accept it fully. 16 * 17 */ 18 19 20 #include <freetype/freetype.h> 21 #include <freetype/internal/ftobjs.h> 22 #include <freetype/internal/ftcalc.h> 23 #include "pshglob.h" 24 25 #ifdef DEBUG_HINTER 26 PSH_Globals ps_debug_globals = NULL; 27 #endif 28 29 30 /*************************************************************************/ 31 /*************************************************************************/ 32 /***** *****/ 33 /***** STANDARD WIDTHS *****/ 34 /***** *****/ 35 /*************************************************************************/ 36 /*************************************************************************/ 37 38 39 /* scale the widths/heights table */ 40 static void psh_globals_scale_widths(PSH_Globals globals,FT_UInt direction)41 psh_globals_scale_widths( PSH_Globals globals, 42 FT_UInt direction ) 43 { 44 PSH_Dimension dim = &globals->dimension[direction]; 45 PSH_Widths stdw = &dim->stdw; 46 FT_UInt count = stdw->count; 47 PSH_Width width = stdw->widths; 48 PSH_Width stand = width; /* standard width/height */ 49 FT_Fixed scale = dim->scale_mult; 50 51 52 if ( count > 0 ) 53 { 54 width->cur = FT_MulFix( width->org, scale ); 55 width->fit = FT_PIX_ROUND( width->cur ); 56 57 width++; 58 count--; 59 60 for ( ; count > 0; count--, width++ ) 61 { 62 FT_Pos w, dist; 63 64 65 w = FT_MulFix( width->org, scale ); 66 dist = w - stand->cur; 67 68 if ( dist < 0 ) 69 dist = -dist; 70 71 if ( dist < 128 ) 72 w = stand->cur; 73 74 width->cur = w; 75 width->fit = FT_PIX_ROUND( w ); 76 } 77 } 78 } 79 80 81 #if 0 82 83 /* org_width is in font units, result in device pixels, 26.6 format */ 84 FT_LOCAL_DEF( FT_Pos ) 85 psh_dimension_snap_width( PSH_Dimension dimension, 86 FT_Int org_width ) 87 { 88 FT_UInt n; 89 FT_Pos width = FT_MulFix( org_width, dimension->scale_mult ); 90 FT_Pos best = 64 + 32 + 2; 91 FT_Pos reference = width; 92 93 94 for ( n = 0; n < dimension->stdw.count; n++ ) 95 { 96 FT_Pos w; 97 FT_Pos dist; 98 99 100 w = dimension->stdw.widths[n].cur; 101 dist = width - w; 102 if ( dist < 0 ) 103 dist = -dist; 104 if ( dist < best ) 105 { 106 best = dist; 107 reference = w; 108 } 109 } 110 111 if ( width >= reference ) 112 { 113 width -= 0x21; 114 if ( width < reference ) 115 width = reference; 116 } 117 else 118 { 119 width += 0x21; 120 if ( width > reference ) 121 width = reference; 122 } 123 124 return width; 125 } 126 127 #endif /* 0 */ 128 129 130 /*************************************************************************/ 131 /*************************************************************************/ 132 /***** *****/ 133 /***** BLUE ZONES *****/ 134 /***** *****/ 135 /*************************************************************************/ 136 /*************************************************************************/ 137 138 static void psh_blues_set_zones_0(PSH_Blues target,FT_Bool is_others,FT_UInt read_count,FT_Short * read,PSH_Blue_Table top_table,PSH_Blue_Table bot_table)139 psh_blues_set_zones_0( PSH_Blues target, 140 FT_Bool is_others, 141 FT_UInt read_count, 142 FT_Short* read, 143 PSH_Blue_Table top_table, 144 PSH_Blue_Table bot_table ) 145 { 146 FT_UInt count_top = top_table->count; 147 FT_UInt count_bot = bot_table->count; 148 FT_Bool first = 1; 149 150 FT_UNUSED( target ); 151 152 153 for ( ; read_count > 1; read_count -= 2 ) 154 { 155 FT_Int reference, delta; 156 FT_UInt count; 157 PSH_Blue_Zone zones, zone; 158 FT_Bool top; 159 160 161 /* read blue zone entry, and select target top/bottom zone */ 162 top = 0; 163 if ( first || is_others ) 164 { 165 reference = read[1]; 166 delta = read[0] - reference; 167 168 zones = bot_table->zones; 169 count = count_bot; 170 first = 0; 171 } 172 else 173 { 174 reference = read[0]; 175 delta = read[1] - reference; 176 177 zones = top_table->zones; 178 count = count_top; 179 top = 1; 180 } 181 182 /* insert into sorted table */ 183 zone = zones; 184 for ( ; count > 0; count--, zone++ ) 185 { 186 if ( reference < zone->org_ref ) 187 break; 188 189 if ( reference == zone->org_ref ) 190 { 191 FT_Int delta0 = zone->org_delta; 192 193 194 /* we have two zones on the same reference position -- */ 195 /* only keep the largest one */ 196 if ( delta < 0 ) 197 { 198 if ( delta < delta0 ) 199 zone->org_delta = delta; 200 } 201 else 202 { 203 if ( delta > delta0 ) 204 zone->org_delta = delta; 205 } 206 goto Skip; 207 } 208 } 209 210 for ( ; count > 0; count-- ) 211 zone[count] = zone[count-1]; 212 213 zone->org_ref = reference; 214 zone->org_delta = delta; 215 216 if ( top ) 217 count_top++; 218 else 219 count_bot++; 220 221 Skip: 222 read += 2; 223 } 224 225 top_table->count = count_top; 226 bot_table->count = count_bot; 227 } 228 229 230 /* Re-read blue zones from the original fonts and store them into our */ 231 /* private structure. This function re-orders, sanitizes, and */ 232 /* fuzz-expands the zones as well. */ 233 static void psh_blues_set_zones(PSH_Blues target,FT_UInt count,FT_Short * blues,FT_UInt count_others,FT_Short * other_blues,FT_Int fuzz,FT_Int family)234 psh_blues_set_zones( PSH_Blues target, 235 FT_UInt count, 236 FT_Short* blues, 237 FT_UInt count_others, 238 FT_Short* other_blues, 239 FT_Int fuzz, 240 FT_Int family ) 241 { 242 PSH_Blue_Table top_table, bot_table; 243 FT_UInt count_top, count_bot; 244 245 246 if ( family ) 247 { 248 top_table = &target->family_top; 249 bot_table = &target->family_bottom; 250 } 251 else 252 { 253 top_table = &target->normal_top; 254 bot_table = &target->normal_bottom; 255 } 256 257 /* read the input blue zones, and build two sorted tables */ 258 /* (one for the top zones, the other for the bottom zones) */ 259 top_table->count = 0; 260 bot_table->count = 0; 261 262 /* first, the blues */ 263 psh_blues_set_zones_0( target, 0, 264 count, blues, top_table, bot_table ); 265 psh_blues_set_zones_0( target, 1, 266 count_others, other_blues, top_table, bot_table ); 267 268 count_top = top_table->count; 269 count_bot = bot_table->count; 270 271 /* sanitize top table */ 272 if ( count_top > 0 ) 273 { 274 PSH_Blue_Zone zone = top_table->zones; 275 276 277 for ( count = count_top; count > 0; count--, zone++ ) 278 { 279 FT_Int delta; 280 281 282 if ( count > 1 ) 283 { 284 delta = zone[1].org_ref - zone[0].org_ref; 285 if ( zone->org_delta > delta ) 286 zone->org_delta = delta; 287 } 288 289 zone->org_bottom = zone->org_ref; 290 zone->org_top = zone->org_delta + zone->org_ref; 291 } 292 } 293 294 /* sanitize bottom table */ 295 if ( count_bot > 0 ) 296 { 297 PSH_Blue_Zone zone = bot_table->zones; 298 299 300 for ( count = count_bot; count > 0; count--, zone++ ) 301 { 302 FT_Int delta; 303 304 305 if ( count > 1 ) 306 { 307 delta = zone[0].org_ref - zone[1].org_ref; 308 if ( zone->org_delta < delta ) 309 zone->org_delta = delta; 310 } 311 312 zone->org_top = zone->org_ref; 313 zone->org_bottom = zone->org_delta + zone->org_ref; 314 } 315 } 316 317 /* expand top and bottom tables with blue fuzz */ 318 { 319 FT_Int dim, top, bot, delta; 320 PSH_Blue_Zone zone; 321 322 323 zone = top_table->zones; 324 count = count_top; 325 326 for ( dim = 1; dim >= 0; dim-- ) 327 { 328 if ( count > 0 ) 329 { 330 /* expand the bottom of the lowest zone normally */ 331 zone->org_bottom -= fuzz; 332 333 /* expand the top and bottom of intermediate zones; */ 334 /* checking that the interval is smaller than the fuzz */ 335 top = zone->org_top; 336 337 for ( count--; count > 0; count-- ) 338 { 339 bot = zone[1].org_bottom; 340 delta = bot - top; 341 342 if ( delta / 2 < fuzz ) 343 zone[0].org_top = zone[1].org_bottom = top + delta / 2; 344 else 345 { 346 zone[0].org_top = top + fuzz; 347 zone[1].org_bottom = bot - fuzz; 348 } 349 350 zone++; 351 top = zone->org_top; 352 } 353 354 /* expand the top of the highest zone normally */ 355 zone->org_top = top + fuzz; 356 } 357 zone = bot_table->zones; 358 count = count_bot; 359 } 360 } 361 } 362 363 364 /* reset the blues table when the device transform changes */ 365 static void psh_blues_scale_zones(PSH_Blues blues,FT_Fixed scale,FT_Pos delta)366 psh_blues_scale_zones( PSH_Blues blues, 367 FT_Fixed scale, 368 FT_Pos delta ) 369 { 370 FT_UInt count; 371 FT_UInt num; 372 PSH_Blue_Table table = NULL; 373 374 /* */ 375 /* Determine whether we need to suppress overshoots or */ 376 /* not. We simply need to compare the vertical scale */ 377 /* parameter to the raw bluescale value. Here is why: */ 378 /* */ 379 /* We need to suppress overshoots for all pointsizes. */ 380 /* At 300dpi that satisfies: */ 381 /* */ 382 /* pointsize < 240*bluescale + 0.49 */ 383 /* */ 384 /* This corresponds to: */ 385 /* */ 386 /* pixelsize < 1000*bluescale + 49/24 */ 387 /* */ 388 /* scale*EM_Size < 1000*bluescale + 49/24 */ 389 /* */ 390 /* However, for normal Type 1 fonts, EM_Size is 1000! */ 391 /* We thus only check: */ 392 /* */ 393 /* scale < bluescale + 49/24000 */ 394 /* */ 395 /* which we shorten to */ 396 /* */ 397 /* "scale < bluescale" */ 398 /* */ 399 /* Note that `blue_scale' is stored 1000 times its real */ 400 /* value, and that `scale' converts from font units to */ 401 /* fractional pixels. */ 402 /* */ 403 404 /* 1000 / 64 = 125 / 8 */ 405 if ( scale >= 0x20C49BAL ) 406 blues->no_overshoots = FT_BOOL( scale < blues->blue_scale * 8 / 125 ); 407 else 408 blues->no_overshoots = FT_BOOL( scale * 125 < blues->blue_scale * 8 ); 409 410 /* */ 411 /* The blue threshold is the font units distance under */ 412 /* which overshoots are suppressed due to the BlueShift */ 413 /* even if the scale is greater than BlueScale. */ 414 /* */ 415 /* It is the smallest distance such that */ 416 /* */ 417 /* dist <= BlueShift && dist*scale <= 0.5 pixels */ 418 /* */ 419 { 420 FT_Int threshold = blues->blue_shift; 421 422 423 while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 ) 424 threshold--; 425 426 blues->blue_threshold = threshold; 427 } 428 429 for ( num = 0; num < 4; num++ ) 430 { 431 PSH_Blue_Zone zone; 432 433 434 switch ( num ) 435 { 436 case 0: 437 table = &blues->normal_top; 438 break; 439 case 1: 440 table = &blues->normal_bottom; 441 break; 442 case 2: 443 table = &blues->family_top; 444 break; 445 default: 446 table = &blues->family_bottom; 447 break; 448 } 449 450 zone = table->zones; 451 count = table->count; 452 for ( ; count > 0; count--, zone++ ) 453 { 454 zone->cur_top = FT_MulFix( zone->org_top, scale ) + delta; 455 zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta; 456 zone->cur_ref = FT_MulFix( zone->org_ref, scale ) + delta; 457 zone->cur_delta = FT_MulFix( zone->org_delta, scale ); 458 459 /* round scaled reference position */ 460 zone->cur_ref = FT_PIX_ROUND( zone->cur_ref ); 461 462 #if 0 463 if ( zone->cur_ref > zone->cur_top ) 464 zone->cur_ref -= 64; 465 else if ( zone->cur_ref < zone->cur_bottom ) 466 zone->cur_ref += 64; 467 #endif 468 } 469 } 470 471 /* process the families now */ 472 473 for ( num = 0; num < 2; num++ ) 474 { 475 PSH_Blue_Zone zone1, zone2; 476 FT_UInt count1, count2; 477 PSH_Blue_Table normal, family; 478 479 480 switch ( num ) 481 { 482 case 0: 483 normal = &blues->normal_top; 484 family = &blues->family_top; 485 break; 486 487 default: 488 normal = &blues->normal_bottom; 489 family = &blues->family_bottom; 490 } 491 492 zone1 = normal->zones; 493 count1 = normal->count; 494 495 for ( ; count1 > 0; count1--, zone1++ ) 496 { 497 /* try to find a family zone whose reference position is less */ 498 /* than 1 pixel far from the current zone */ 499 zone2 = family->zones; 500 count2 = family->count; 501 502 for ( ; count2 > 0; count2--, zone2++ ) 503 { 504 FT_Pos Delta; 505 506 507 Delta = zone1->org_ref - zone2->org_ref; 508 if ( Delta < 0 ) 509 Delta = -Delta; 510 511 if ( FT_MulFix( Delta, scale ) < 64 ) 512 { 513 zone1->cur_top = zone2->cur_top; 514 zone1->cur_bottom = zone2->cur_bottom; 515 zone1->cur_ref = zone2->cur_ref; 516 zone1->cur_delta = zone2->cur_delta; 517 break; 518 } 519 } 520 } 521 } 522 } 523 524 525 /* calculate the maximum height of given blue zones */ 526 static FT_Short psh_calc_max_height(FT_UInt num,const FT_Short * values,FT_Short cur_max)527 psh_calc_max_height( FT_UInt num, 528 const FT_Short* values, 529 FT_Short cur_max ) 530 { 531 FT_UInt count; 532 533 534 for ( count = 0; count < num; count += 2 ) 535 { 536 FT_Short cur_height = values[count + 1] - values[count]; 537 538 539 if ( cur_height > cur_max ) 540 cur_max = cur_height; 541 } 542 543 return cur_max; 544 } 545 546 547 FT_LOCAL_DEF( void ) psh_blues_snap_stem(PSH_Blues blues,FT_Int stem_top,FT_Int stem_bot,PSH_Alignment alignment)548 psh_blues_snap_stem( PSH_Blues blues, 549 FT_Int stem_top, 550 FT_Int stem_bot, 551 PSH_Alignment alignment ) 552 { 553 PSH_Blue_Table table; 554 FT_UInt count; 555 FT_Pos delta; 556 PSH_Blue_Zone zone; 557 FT_Int no_shoots; 558 559 560 alignment->align = PSH_BLUE_ALIGN_NONE; 561 562 no_shoots = blues->no_overshoots; 563 564 /* look up stem top in top zones table */ 565 table = &blues->normal_top; 566 count = table->count; 567 zone = table->zones; 568 569 for ( ; count > 0; count--, zone++ ) 570 { 571 delta = SUB_LONG( stem_top, zone->org_bottom ); 572 if ( delta < -blues->blue_fuzz ) 573 break; 574 575 if ( stem_top <= zone->org_top + blues->blue_fuzz ) 576 { 577 if ( no_shoots || delta <= blues->blue_threshold ) 578 { 579 alignment->align |= PSH_BLUE_ALIGN_TOP; 580 alignment->align_top = zone->cur_ref; 581 } 582 break; 583 } 584 } 585 586 /* look up stem bottom in bottom zones table */ 587 table = &blues->normal_bottom; 588 count = table->count; 589 zone = table->zones + count-1; 590 591 for ( ; count > 0; count--, zone-- ) 592 { 593 delta = SUB_LONG( zone->org_top, stem_bot ); 594 if ( delta < -blues->blue_fuzz ) 595 break; 596 597 if ( stem_bot >= zone->org_bottom - blues->blue_fuzz ) 598 { 599 if ( no_shoots || delta < blues->blue_threshold ) 600 { 601 alignment->align |= PSH_BLUE_ALIGN_BOT; 602 alignment->align_bot = zone->cur_ref; 603 } 604 break; 605 } 606 } 607 } 608 609 610 /*************************************************************************/ 611 /*************************************************************************/ 612 /***** *****/ 613 /***** GLOBAL HINTS *****/ 614 /***** *****/ 615 /*************************************************************************/ 616 /*************************************************************************/ 617 618 static void psh_globals_destroy(PSH_Globals globals)619 psh_globals_destroy( PSH_Globals globals ) 620 { 621 if ( globals ) 622 { 623 FT_Memory memory; 624 625 626 memory = globals->memory; 627 globals->dimension[0].stdw.count = 0; 628 globals->dimension[1].stdw.count = 0; 629 630 globals->blues.normal_top.count = 0; 631 globals->blues.normal_bottom.count = 0; 632 globals->blues.family_top.count = 0; 633 globals->blues.family_bottom.count = 0; 634 635 FT_FREE( globals ); 636 637 #ifdef DEBUG_HINTER 638 ps_debug_globals = NULL; 639 #endif 640 } 641 } 642 643 644 static FT_Error psh_globals_new(FT_Memory memory,T1_Private * priv,PSH_Globals * aglobals)645 psh_globals_new( FT_Memory memory, 646 T1_Private* priv, 647 PSH_Globals *aglobals ) 648 { 649 PSH_Globals globals = NULL; 650 FT_Error error; 651 652 653 if ( !FT_NEW( globals ) ) 654 { 655 FT_UInt count; 656 FT_Short* read; 657 658 659 globals->memory = memory; 660 661 /* copy standard widths */ 662 { 663 PSH_Dimension dim = &globals->dimension[1]; 664 PSH_Width write = dim->stdw.widths; 665 666 667 write->org = priv->standard_width[0]; 668 write++; 669 670 read = priv->snap_widths; 671 for ( count = priv->num_snap_widths; count > 0; count-- ) 672 { 673 write->org = *read; 674 write++; 675 read++; 676 } 677 678 dim->stdw.count = priv->num_snap_widths + 1; 679 } 680 681 /* copy standard heights */ 682 { 683 PSH_Dimension dim = &globals->dimension[0]; 684 PSH_Width write = dim->stdw.widths; 685 686 687 write->org = priv->standard_height[0]; 688 write++; 689 read = priv->snap_heights; 690 for ( count = priv->num_snap_heights; count > 0; count-- ) 691 { 692 write->org = *read; 693 write++; 694 read++; 695 } 696 697 dim->stdw.count = priv->num_snap_heights + 1; 698 } 699 700 /* copy blue zones */ 701 psh_blues_set_zones( &globals->blues, priv->num_blue_values, 702 priv->blue_values, priv->num_other_blues, 703 priv->other_blues, priv->blue_fuzz, 0 ); 704 705 psh_blues_set_zones( &globals->blues, priv->num_family_blues, 706 priv->family_blues, priv->num_family_other_blues, 707 priv->family_other_blues, priv->blue_fuzz, 1 ); 708 709 /* limit the BlueScale value to `1 / max_of_blue_zone_heights' */ 710 { 711 FT_Fixed max_scale; 712 FT_Short max_height = 1; 713 714 715 max_height = psh_calc_max_height( priv->num_blue_values, 716 priv->blue_values, 717 max_height ); 718 max_height = psh_calc_max_height( priv->num_other_blues, 719 priv->other_blues, 720 max_height ); 721 max_height = psh_calc_max_height( priv->num_family_blues, 722 priv->family_blues, 723 max_height ); 724 max_height = psh_calc_max_height( priv->num_family_other_blues, 725 priv->family_other_blues, 726 max_height ); 727 728 /* BlueScale is scaled 1000 times */ 729 max_scale = FT_DivFix( 1000, max_height ); 730 globals->blues.blue_scale = priv->blue_scale < max_scale 731 ? priv->blue_scale 732 : max_scale; 733 } 734 735 globals->blues.blue_shift = priv->blue_shift; 736 globals->blues.blue_fuzz = priv->blue_fuzz; 737 738 globals->dimension[0].scale_mult = 0; 739 globals->dimension[0].scale_delta = 0; 740 globals->dimension[1].scale_mult = 0; 741 globals->dimension[1].scale_delta = 0; 742 743 #ifdef DEBUG_HINTER 744 ps_debug_globals = globals; 745 #endif 746 } 747 748 *aglobals = globals; 749 return error; 750 } 751 752 753 FT_LOCAL_DEF( void ) psh_globals_set_scale(PSH_Globals globals,FT_Fixed x_scale,FT_Fixed y_scale,FT_Fixed x_delta,FT_Fixed y_delta)754 psh_globals_set_scale( PSH_Globals globals, 755 FT_Fixed x_scale, 756 FT_Fixed y_scale, 757 FT_Fixed x_delta, 758 FT_Fixed y_delta ) 759 { 760 PSH_Dimension dim; 761 762 763 dim = &globals->dimension[0]; 764 if ( x_scale != dim->scale_mult || 765 x_delta != dim->scale_delta ) 766 { 767 dim->scale_mult = x_scale; 768 dim->scale_delta = x_delta; 769 770 psh_globals_scale_widths( globals, 0 ); 771 } 772 773 dim = &globals->dimension[1]; 774 if ( y_scale != dim->scale_mult || 775 y_delta != dim->scale_delta ) 776 { 777 dim->scale_mult = y_scale; 778 dim->scale_delta = y_delta; 779 780 psh_globals_scale_widths( globals, 1 ); 781 psh_blues_scale_zones( &globals->blues, y_scale, y_delta ); 782 } 783 } 784 785 786 FT_LOCAL_DEF( void ) psh_globals_funcs_init(PSH_Globals_FuncsRec * funcs)787 psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs ) 788 { 789 funcs->create = psh_globals_new; 790 funcs->set_scale = psh_globals_set_scale; 791 funcs->destroy = psh_globals_destroy; 792 } 793 794 795 /* END */ 796