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, 2003, 2004, 2006, 2010 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 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 } 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 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 = 0; 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 FT_LOCAL_DEF( void ) 526 psh_blues_snap_stem( PSH_Blues blues, 527 FT_Int stem_top, 528 FT_Int stem_bot, 529 PSH_Alignment alignment ) 530 { 531 PSH_Blue_Table table; 532 FT_UInt count; 533 FT_Pos delta; 534 PSH_Blue_Zone zone; 535 FT_Int no_shoots; 536 537 538 alignment->align = PSH_BLUE_ALIGN_NONE; 539 540 no_shoots = blues->no_overshoots; 541 542 /* look up stem top in top zones table */ 543 table = &blues->normal_top; 544 count = table->count; 545 zone = table->zones; 546 547 for ( ; count > 0; count--, zone++ ) 548 { 549 delta = stem_top - zone->org_bottom; 550 if ( delta < -blues->blue_fuzz ) 551 break; 552 553 if ( stem_top <= zone->org_top + blues->blue_fuzz ) 554 { 555 if ( no_shoots || delta <= blues->blue_threshold ) 556 { 557 alignment->align |= PSH_BLUE_ALIGN_TOP; 558 alignment->align_top = zone->cur_ref; 559 } 560 break; 561 } 562 } 563 564 /* look up stem bottom in bottom zones table */ 565 table = &blues->normal_bottom; 566 count = table->count; 567 zone = table->zones + count-1; 568 569 for ( ; count > 0; count--, zone-- ) 570 { 571 delta = zone->org_top - stem_bot; 572 if ( delta < -blues->blue_fuzz ) 573 break; 574 575 if ( stem_bot >= zone->org_bottom - blues->blue_fuzz ) 576 { 577 if ( no_shoots || delta < blues->blue_threshold ) 578 { 579 alignment->align |= PSH_BLUE_ALIGN_BOT; 580 alignment->align_bot = zone->cur_ref; 581 } 582 break; 583 } 584 } 585 } 586 587 588 /*************************************************************************/ 589 /*************************************************************************/ 590 /***** *****/ 591 /***** GLOBAL HINTS *****/ 592 /***** *****/ 593 /*************************************************************************/ 594 /*************************************************************************/ 595 596 static void 597 psh_globals_destroy( PSH_Globals globals ) 598 { 599 if ( globals ) 600 { 601 FT_Memory memory; 602 603 604 memory = globals->memory; 605 globals->dimension[0].stdw.count = 0; 606 globals->dimension[1].stdw.count = 0; 607 608 globals->blues.normal_top.count = 0; 609 globals->blues.normal_bottom.count = 0; 610 globals->blues.family_top.count = 0; 611 globals->blues.family_bottom.count = 0; 612 613 FT_FREE( globals ); 614 615#ifdef DEBUG_HINTER 616 ps_debug_globals = 0; 617#endif 618 } 619 } 620 621 622 static FT_Error 623 psh_globals_new( FT_Memory memory, 624 T1_Private* priv, 625 PSH_Globals *aglobals ) 626 { 627 PSH_Globals globals = NULL; 628 FT_Error error; 629 630 631 if ( !FT_NEW( globals ) ) 632 { 633 FT_UInt count; 634 FT_Short* read; 635 636 637 globals->memory = memory; 638 639 /* copy standard widths */ 640 { 641 PSH_Dimension dim = &globals->dimension[1]; 642 PSH_Width write = dim->stdw.widths; 643 644 645 write->org = priv->standard_width[0]; 646 write++; 647 648 read = priv->snap_widths; 649 for ( count = priv->num_snap_widths; count > 0; count-- ) 650 { 651 write->org = *read; 652 write++; 653 read++; 654 } 655 656 dim->stdw.count = priv->num_snap_widths + 1; 657 } 658 659 /* copy standard heights */ 660 { 661 PSH_Dimension dim = &globals->dimension[0]; 662 PSH_Width write = dim->stdw.widths; 663 664 665 write->org = priv->standard_height[0]; 666 write++; 667 read = priv->snap_heights; 668 for ( count = priv->num_snap_heights; count > 0; count-- ) 669 { 670 write->org = *read; 671 write++; 672 read++; 673 } 674 675 dim->stdw.count = priv->num_snap_heights + 1; 676 } 677 678 /* copy blue zones */ 679 psh_blues_set_zones( &globals->blues, priv->num_blue_values, 680 priv->blue_values, priv->num_other_blues, 681 priv->other_blues, priv->blue_fuzz, 0 ); 682 683 psh_blues_set_zones( &globals->blues, priv->num_family_blues, 684 priv->family_blues, priv->num_family_other_blues, 685 priv->family_other_blues, priv->blue_fuzz, 1 ); 686 687 globals->blues.blue_scale = priv->blue_scale; 688 globals->blues.blue_shift = priv->blue_shift; 689 globals->blues.blue_fuzz = priv->blue_fuzz; 690 691 globals->dimension[0].scale_mult = 0; 692 globals->dimension[0].scale_delta = 0; 693 globals->dimension[1].scale_mult = 0; 694 globals->dimension[1].scale_delta = 0; 695 696#ifdef DEBUG_HINTER 697 ps_debug_globals = globals; 698#endif 699 } 700 701 *aglobals = globals; 702 return error; 703 } 704 705 706 FT_LOCAL_DEF( FT_Error ) 707 psh_globals_set_scale( PSH_Globals globals, 708 FT_Fixed x_scale, 709 FT_Fixed y_scale, 710 FT_Fixed x_delta, 711 FT_Fixed y_delta ) 712 { 713 PSH_Dimension dim = &globals->dimension[0]; 714 715 716 dim = &globals->dimension[0]; 717 if ( x_scale != dim->scale_mult || 718 x_delta != dim->scale_delta ) 719 { 720 dim->scale_mult = x_scale; 721 dim->scale_delta = x_delta; 722 723 psh_globals_scale_widths( globals, 0 ); 724 } 725 726 dim = &globals->dimension[1]; 727 if ( y_scale != dim->scale_mult || 728 y_delta != dim->scale_delta ) 729 { 730 dim->scale_mult = y_scale; 731 dim->scale_delta = y_delta; 732 733 psh_globals_scale_widths( globals, 1 ); 734 psh_blues_scale_zones( &globals->blues, y_scale, y_delta ); 735 } 736 737 return 0; 738 } 739 740 741 FT_LOCAL_DEF( void ) 742 psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs ) 743 { 744 funcs->create = psh_globals_new; 745 funcs->set_scale = psh_globals_set_scale; 746 funcs->destroy = psh_globals_destroy; 747 } 748 749 750/* END */ 751