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