1/***************************************************************************/ 2/* */ 3/* pshglob.c */ 4/* */ 5/* PostScript hinter global hinting management (body). */ 6/* Inspired by the new auto-hinter module. */ 7/* */ 8/* Copyright 2001-2004, 2006, 2010, 2012 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 "../../include/ft2build.h" 21#include "../../include/freetype/freetype.h" 22#include "../../include/freetype/internal/ftobjs.h" 23#include "pshglob.h" 24 25#ifdef DEBUG_HINTER 26 PSH_Globals ps_debug_globals = 0; 27#endif 28 29 30 /*************************************************************************/ 31 /*************************************************************************/ 32 /***** *****/ 33 /***** STANDARD WIDTHS *****/ 34 /***** *****/ 35 /*************************************************************************/ 36 /*************************************************************************/ 37 38 39 /* scale the widths/heights table */ 40 static void 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 is 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 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 out */ 231 /* private structure. This function re-orders, sanitizes and */ 232 /* fuzz-expands the zones as well. */ 233 static void 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_Int 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 // XYQ 2006-3-11: FT PS hinter doesn't process wide zone well (all points shrinked to one horizontal line) 315 // therefore, we have to do some nasty thing here, because some fonts do give wide bottom zones. 316 // Our principle is to preserve the bottom line 317 // TEST DOC: bug#395 csl.pdf letters "y", "j", etc. 318 if (zone->org_top - zone->org_bottom > 10) { 319 zone->org_top = zone->org_bottom; 320 zone->org_delta = 0; 321 } 322 } 323 } 324 325 /* expand top and bottom tables with blue fuzz */ 326 { 327 FT_Int dim, top, bot, delta; 328 PSH_Blue_Zone zone; 329 330 331 zone = top_table->zones; 332 count = count_top; 333 334 for ( dim = 1; dim >= 0; dim-- ) 335 { 336 if ( count > 0 ) 337 { 338 /* expand the bottom of the lowest zone normally */ 339 zone->org_bottom -= fuzz; 340 341 /* expand the top and bottom of intermediate zones; */ 342 /* checking that the interval is smaller than the fuzz */ 343 top = zone->org_top; 344 345 for ( count--; count > 0; count-- ) 346 { 347 bot = zone[1].org_bottom; 348 delta = bot - top; 349 350 if ( delta < 2 * fuzz ) 351 zone[0].org_top = zone[1].org_bottom = top + delta / 2; 352 else 353 { 354 zone[0].org_top = top + fuzz; 355 zone[1].org_bottom = bot - fuzz; 356 } 357 358 zone++; 359 top = zone->org_top; 360 } 361 362 /* expand the top of the highest zone normally */ 363 zone->org_top = top + fuzz; 364 } 365 zone = bot_table->zones; 366 count = count_bot; 367 } 368 } 369 } 370 371 372 /* reset the blues table when the device transform changes */ 373 static void 374 psh_blues_scale_zones( PSH_Blues blues, 375 FT_Fixed scale, 376 FT_Pos delta ) 377 { 378 FT_UInt count; 379 FT_UInt num; 380 PSH_Blue_Table table = 0; 381 382 /* */ 383 /* Determine whether we need to suppress overshoots or */ 384 /* not. We simply need to compare the vertical scale */ 385 /* parameter to the raw bluescale value. Here is why: */ 386 /* */ 387 /* We need to suppress overshoots for all pointsizes. */ 388 /* At 300dpi that satisfies: */ 389 /* */ 390 /* pointsize < 240*bluescale + 0.49 */ 391 /* */ 392 /* This corresponds to: */ 393 /* */ 394 /* pixelsize < 1000*bluescale + 49/24 */ 395 /* */ 396 /* scale*EM_Size < 1000*bluescale + 49/24 */ 397 /* */ 398 /* However, for normal Type 1 fonts, EM_Size is 1000! */ 399 /* We thus only check: */ 400 /* */ 401 /* scale < bluescale + 49/24000 */ 402 /* */ 403 /* which we shorten to */ 404 /* */ 405 /* "scale < bluescale" */ 406 /* */ 407 /* Note that `blue_scale' is stored 1000 times its real */ 408 /* value, and that `scale' converts from font units to */ 409 /* fractional pixels. */ 410 /* */ 411 412 /* 1000 / 64 = 125 / 8 */ 413 if ( scale >= 0x20C49BAL ) 414 blues->no_overshoots = FT_BOOL( scale < blues->blue_scale * 8 / 125 ); 415 else 416 blues->no_overshoots = FT_BOOL( scale * 125 < blues->blue_scale * 8 ); 417 418 /* */ 419 /* The blue threshold is the font units distance under */ 420 /* which overshoots are suppressed due to the BlueShift */ 421 /* even if the scale is greater than BlueScale. */ 422 /* */ 423 /* It is the smallest distance such that */ 424 /* */ 425 /* dist <= BlueShift && dist*scale <= 0.5 pixels */ 426 /* */ 427 { 428 FT_Int threshold = blues->blue_shift; 429 430 431 while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 ) 432 threshold--; 433 434 blues->blue_threshold = threshold; 435 } 436 437 for ( num = 0; num < 4; num++ ) 438 { 439 PSH_Blue_Zone zone; 440 441 442 switch ( num ) 443 { 444 case 0: 445 table = &blues->normal_top; 446 break; 447 case 1: 448 table = &blues->normal_bottom; 449 break; 450 case 2: 451 table = &blues->family_top; 452 break; 453 default: 454 table = &blues->family_bottom; 455 break; 456 } 457 458 zone = table->zones; 459 count = table->count; 460 for ( ; count > 0; count--, zone++ ) 461 { 462 zone->cur_top = FT_MulFix( zone->org_top, scale ) + delta; 463 zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta; 464 zone->cur_ref = FT_MulFix( zone->org_ref, scale ) + delta; 465 zone->cur_delta = FT_MulFix( zone->org_delta, scale ); 466 467 /* round scaled reference position */ 468 zone->cur_ref = FT_PIX_ROUND( zone->cur_ref ); 469 470#if 0 471 if ( zone->cur_ref > zone->cur_top ) 472 zone->cur_ref -= 64; 473 else if ( zone->cur_ref < zone->cur_bottom ) 474 zone->cur_ref += 64; 475#endif 476 } 477 } 478 479 /* process the families now */ 480 481 for ( num = 0; num < 2; num++ ) 482 { 483 PSH_Blue_Zone zone1, zone2; 484 FT_UInt count1, count2; 485 PSH_Blue_Table normal, family; 486 487 488 switch ( num ) 489 { 490 case 0: 491 normal = &blues->normal_top; 492 family = &blues->family_top; 493 break; 494 495 default: 496 normal = &blues->normal_bottom; 497 family = &blues->family_bottom; 498 } 499 500 zone1 = normal->zones; 501 count1 = normal->count; 502 503 for ( ; count1 > 0; count1--, zone1++ ) 504 { 505 /* try to find a family zone whose reference position is less */ 506 /* than 1 pixel far from the current zone */ 507 zone2 = family->zones; 508 count2 = family->count; 509 510 for ( ; count2 > 0; count2--, zone2++ ) 511 { 512 FT_Pos Delta; 513 514 515 Delta = zone1->org_ref - zone2->org_ref; 516 if ( Delta < 0 ) 517 Delta = -Delta; 518 519 if ( FT_MulFix( Delta, scale ) < 64 ) 520 { 521 zone1->cur_top = zone2->cur_top; 522 zone1->cur_bottom = zone2->cur_bottom; 523 zone1->cur_ref = zone2->cur_ref; 524 zone1->cur_delta = zone2->cur_delta; 525 break; 526 } 527 } 528 } 529 } 530 } 531 532 533 /* calculate the maximum height of given blue zones */ 534 static FT_Short 535 psh_calc_max_height( FT_UInt num, 536 const FT_Short* values, 537 FT_Short cur_max ) 538 { 539 FT_UInt count; 540 541 542 for ( count = 0; count < num; count += 2 ) 543 { 544 FT_Short cur_height = values[count + 1] - values[count]; 545 546 547 if ( cur_height > cur_max ) 548 cur_max = cur_height; 549 } 550 551 return cur_max; 552 } 553 554 555 FT_LOCAL_DEF( void ) 556 psh_blues_snap_stem( PSH_Blues blues, 557 FT_Int stem_top, 558 FT_Int stem_bot, 559 PSH_Alignment alignment ) 560 { 561 PSH_Blue_Table table; 562 FT_UInt count; 563 FT_Pos delta; 564 PSH_Blue_Zone zone; 565 FT_Int no_shoots; 566 567 568 alignment->align = PSH_BLUE_ALIGN_NONE; 569 570 no_shoots = blues->no_overshoots; 571 572 /* look up stem top in top zones table */ 573 table = &blues->normal_top; 574 count = table->count; 575 zone = table->zones; 576 577 for ( ; count > 0; count--, zone++ ) 578 { 579 delta = stem_top - zone->org_bottom; 580 if ( delta < -blues->blue_fuzz ) 581 break; 582 583 if ( stem_top <= zone->org_top + blues->blue_fuzz ) 584 { 585 if ( no_shoots || delta <= blues->blue_threshold ) 586 { 587 alignment->align |= PSH_BLUE_ALIGN_TOP; 588 alignment->align_top = zone->cur_ref; 589 } 590 break; 591 } 592 } 593 594 /* look up stem bottom in bottom zones table */ 595 table = &blues->normal_bottom; 596 count = table->count; 597 zone = table->zones + count-1; 598 599 for ( ; count > 0; count--, zone-- ) 600 { 601 delta = zone->org_top - stem_bot; 602 if ( delta < -blues->blue_fuzz ) 603 break; 604 605 if ( stem_bot >= zone->org_bottom - blues->blue_fuzz ) 606 { 607 if ( no_shoots || delta < blues->blue_threshold ) 608 { 609 alignment->align |= PSH_BLUE_ALIGN_BOT; 610 alignment->align_bot = zone->cur_ref; 611 } 612 break; 613 } 614 } 615 } 616 617 618 /*************************************************************************/ 619 /*************************************************************************/ 620 /***** *****/ 621 /***** GLOBAL HINTS *****/ 622 /***** *****/ 623 /*************************************************************************/ 624 /*************************************************************************/ 625 626 static void 627 psh_globals_destroy( PSH_Globals globals ) 628 { 629 if ( globals ) 630 { 631 FT_Memory memory; 632 633 634 memory = globals->memory; 635 globals->dimension[0].stdw.count = 0; 636 globals->dimension[1].stdw.count = 0; 637 638 globals->blues.normal_top.count = 0; 639 globals->blues.normal_bottom.count = 0; 640 globals->blues.family_top.count = 0; 641 globals->blues.family_bottom.count = 0; 642 643 FT_FREE( globals ); 644 645#ifdef DEBUG_HINTER 646 ps_debug_globals = 0; 647#endif 648 } 649 } 650 651 652 static FT_Error 653 psh_globals_new( FT_Memory memory, 654 T1_Private* priv, 655 PSH_Globals *aglobals ) 656 { 657 PSH_Globals globals = NULL; 658 FT_Error error; 659 660 661 if ( !FT_NEW( globals ) ) 662 { 663 FT_UInt count; 664 FT_Short* read; 665 666 667 globals->memory = memory; 668 669 /* copy standard widths */ 670 { 671 PSH_Dimension dim = &globals->dimension[1]; 672 PSH_Width write = dim->stdw.widths; 673 674 675 write->org = priv->standard_width[0]; 676 write++; 677 678 read = priv->snap_widths; 679 for ( count = priv->num_snap_widths; count > 0; count-- ) 680 { 681 write->org = *read; 682 write++; 683 read++; 684 } 685 686 dim->stdw.count = priv->num_snap_widths + 1; 687 } 688 689 /* copy standard heights */ 690 { 691 PSH_Dimension dim = &globals->dimension[0]; 692 PSH_Width write = dim->stdw.widths; 693 694 695 write->org = priv->standard_height[0]; 696 write++; 697 read = priv->snap_heights; 698 for ( count = priv->num_snap_heights; count > 0; count-- ) 699 { 700 write->org = *read; 701 write++; 702 read++; 703 } 704 705 dim->stdw.count = priv->num_snap_heights + 1; 706 } 707 708 /* copy blue zones */ 709 psh_blues_set_zones( &globals->blues, priv->num_blue_values, 710 priv->blue_values, priv->num_other_blues, 711 priv->other_blues, priv->blue_fuzz, 0 ); 712 713 psh_blues_set_zones( &globals->blues, priv->num_family_blues, 714 priv->family_blues, priv->num_family_other_blues, 715 priv->family_other_blues, priv->blue_fuzz, 1 ); 716 717 /* limit the BlueScale value to `1 / max_of_blue_zone_heights' */ 718 { 719 FT_Fixed max_scale; 720 FT_Short max_height = 1; 721 722 723 max_height = psh_calc_max_height( priv->num_blue_values, 724 priv->blue_values, 725 max_height ); 726 max_height = psh_calc_max_height( priv->num_other_blues, 727 priv->other_blues, 728 max_height ); 729 max_height = psh_calc_max_height( priv->num_family_blues, 730 priv->family_blues, 731 max_height ); 732 max_height = psh_calc_max_height( priv->num_family_other_blues, 733 priv->family_other_blues, 734 max_height ); 735 736 /* BlueScale is scaled 1000 times */ 737 max_scale = FT_DivFix( 1000, max_height ); 738 globals->blues.blue_scale = priv->blue_scale < max_scale 739 ? priv->blue_scale 740 : max_scale; 741 } 742 743 globals->blues.blue_shift = priv->blue_shift; 744 globals->blues.blue_fuzz = priv->blue_fuzz; 745 746 globals->dimension[0].scale_mult = 0; 747 globals->dimension[0].scale_delta = 0; 748 globals->dimension[1].scale_mult = 0; 749 globals->dimension[1].scale_delta = 0; 750 751#ifdef DEBUG_HINTER 752 ps_debug_globals = globals; 753#endif 754 } 755 756 *aglobals = globals; 757 return error; 758 } 759 760 761 FT_LOCAL_DEF( FT_Error ) 762 psh_globals_set_scale( PSH_Globals globals, 763 FT_Fixed x_scale, 764 FT_Fixed y_scale, 765 FT_Fixed x_delta, 766 FT_Fixed y_delta ) 767 { 768 PSH_Dimension dim = &globals->dimension[0]; 769 770 771 dim = &globals->dimension[0]; 772 if ( x_scale != dim->scale_mult || 773 x_delta != dim->scale_delta ) 774 { 775 dim->scale_mult = x_scale; 776 dim->scale_delta = x_delta; 777 778 psh_globals_scale_widths( globals, 0 ); 779 } 780 781 dim = &globals->dimension[1]; 782 if ( y_scale != dim->scale_mult || 783 y_delta != dim->scale_delta ) 784 { 785 dim->scale_mult = y_scale; 786 dim->scale_delta = y_delta; 787 788 psh_globals_scale_widths( globals, 1 ); 789 psh_blues_scale_zones( &globals->blues, y_scale, y_delta ); 790 } 791 792 return 0; 793 } 794 795 796 FT_LOCAL_DEF( void ) 797 psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs ) 798 { 799 funcs->create = psh_globals_new; 800 funcs->set_scale = psh_globals_set_scale; 801 funcs->destroy = psh_globals_destroy; 802 } 803 804 805/* END */ 806