1 /***************************************************************************/ 2 /* */ 3 /* ahhint.c */ 4 /* */ 5 /* Glyph hinter (body). */ 6 /* */ 7 /* Copyright 2000-2001, 2002 Catharon Productions Inc. */ 8 /* Author: David Turner */ 9 /* */ 10 /* This file is part of the Catharon Typography Project and shall only */ 11 /* be used, modified, and distributed under the terms of the Catharon */ 12 /* Open Source License that should come with this file under the name */ 13 /* `CatharonLicense.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 /* Note that this license is compatible with the FreeType license. */ 18 /* */ 19 /***************************************************************************/ 20 21 22 #include <ft2build.h> 23 #include "ahhint.h" 24 #include "ahglyph.h" 25 #include "ahangles.h" 26 #include "aherrors.h" 27 #include FT_OUTLINE_H 28 29 30 #define FACE_GLOBALS( face ) ((AH_Face_Globals)(face)->autohint.data) 31 32 #define AH_USE_IUP 33 #define OPTIM_STEM_SNAP 34 35 /*************************************************************************/ 36 /*************************************************************************/ 37 /**** ****/ 38 /**** Hinting routines ****/ 39 /**** ****/ 40 /*************************************************************************/ 41 /*************************************************************************/ 42 43 /* snap a given width in scaled coordinates to one of the */ 44 /* current standard widths */ 45 static FT_Pos ah_snap_width(FT_Pos * widths,FT_Int count,FT_Pos width)46 ah_snap_width( FT_Pos* widths, 47 FT_Int count, 48 FT_Pos width ) 49 { 50 int n; 51 FT_Pos best = 64 + 32 + 2; 52 FT_Pos reference = width; 53 FT_Pos scaled; 54 55 56 for ( n = 0; n < count; n++ ) 57 { 58 FT_Pos w; 59 FT_Pos dist; 60 61 62 w = widths[n]; 63 dist = width - w; 64 if ( dist < 0 ) 65 dist = -dist; 66 if ( dist < best ) 67 { 68 best = dist; 69 reference = w; 70 } 71 } 72 73 scaled = (reference+32) & -64; 74 75 if ( width >= reference ) 76 { 77 if ( width < scaled + 48 ) 78 width = reference; 79 } 80 else 81 { 82 if ( width > scaled - 48 ) 83 width = reference; 84 } 85 86 return width; 87 } 88 89 90 /* compute the snapped width of a given stem */ 91 static FT_Pos ah_compute_stem_width(AH_Hinter hinter,int vertical,FT_Pos width)92 ah_compute_stem_width( AH_Hinter hinter, 93 int vertical, 94 FT_Pos width ) 95 { 96 AH_Globals globals = &hinter->globals->scaled; 97 FT_Pos dist = width; 98 FT_Int sign = 0; 99 100 101 if ( dist < 0 ) 102 { 103 dist = -width; 104 sign = 1; 105 } 106 107 if ( ( vertical && !hinter->do_vert_snapping ) || 108 ( !vertical && !hinter->do_horz_snapping ) ) 109 { 110 /* smooth hinting process, very lightly quantize the stem width */ 111 /* */ 112 if ( dist < 64 ) 113 dist = 64; 114 115 { 116 FT_Pos delta = dist - globals->stds[vertical]; 117 118 119 if ( delta < 0 ) 120 delta = -delta; 121 122 if ( delta < 40 ) 123 { 124 dist = globals->stds[vertical]; 125 if ( dist < 48 ) 126 dist = 48; 127 } 128 129 if ( dist < 3 * 64 ) 130 { 131 delta = ( dist & 63 ); 132 dist &= -64; 133 134 if ( delta < 10 ) 135 dist += delta; 136 137 else if ( delta < 32 ) 138 dist += 10; 139 140 else if ( delta < 54 ) 141 dist += 54; 142 143 else 144 dist += delta; 145 } 146 else 147 dist = ( dist + 32 ) & -64; 148 } 149 } 150 else 151 { 152 /* strong hinting process, snap the stem width to integer pixels */ 153 /* */ 154 if ( vertical ) 155 { 156 dist = ah_snap_width( globals->heights, globals->num_heights, dist ); 157 158 /* in the case of vertical hinting, always round */ 159 /* the stem heights to integer pixels */ 160 if ( dist >= 64 ) 161 dist = ( dist + 16 ) & -64; 162 else 163 dist = 64; 164 } 165 else 166 { 167 dist = ah_snap_width( globals->widths, globals->num_widths, dist ); 168 169 if ( hinter->flags & AH_HINTER_MONOCHROME ) 170 { 171 /* monochrome horizontal hinting: snap widths to integer pixels */ 172 /* with a different threshold */ 173 if ( dist < 64 ) 174 dist = 64; 175 else 176 dist = ( dist + 32 ) & -64; 177 } 178 else 179 { 180 /* for horizontal anti-aliased hinting, we adopt a more subtle */ 181 /* approach: we strengthen small stems, round stems whose size */ 182 /* is between 1 and 2 pixels to an integer, otherwise nothing */ 183 if ( dist < 48 ) 184 dist = ( dist + 64 ) >> 1; 185 186 else if ( dist < 128 ) 187 dist = ( dist + 22 ) & -64; 188 else 189 /* XXX: round otherwise, prevent color fringes in LCD mode */ 190 dist = ( dist + 32 ) & -64; 191 } 192 } 193 } 194 195 if ( sign ) 196 dist = -dist; 197 198 return dist; 199 } 200 201 202 /* align one stem edge relative to the previous stem edge */ 203 static void ah_align_linked_edge(AH_Hinter hinter,AH_Edge base_edge,AH_Edge stem_edge,int vertical)204 ah_align_linked_edge( AH_Hinter hinter, 205 AH_Edge base_edge, 206 AH_Edge stem_edge, 207 int vertical ) 208 { 209 FT_Pos dist = stem_edge->opos - base_edge->opos; 210 211 212 stem_edge->pos = base_edge->pos + 213 ah_compute_stem_width( hinter, vertical, dist ); 214 } 215 216 217 static void ah_align_serif_edge(AH_Hinter hinter,AH_Edge base,AH_Edge serif,int vertical)218 ah_align_serif_edge( AH_Hinter hinter, 219 AH_Edge base, 220 AH_Edge serif, 221 int vertical ) 222 { 223 FT_Pos dist; 224 FT_Pos sign = 1; 225 226 FT_UNUSED( hinter ); 227 228 229 dist = serif->opos - base->opos; 230 if ( dist < 0 ) 231 { 232 dist = -dist; 233 sign = -1; 234 } 235 236 /* do not touch serifs widths !! */ 237 #if 0 238 if ( base->flags & AH_EDGE_DONE ) 239 { 240 if ( dist >= 64 ) 241 dist = (dist+8) & -64; 242 243 else if ( dist <= 32 && !vertical ) 244 dist = ( dist + 33 ) >> 1; 245 else 246 dist = 0; 247 } 248 #endif 249 250 serif->pos = base->pos + sign * dist; 251 } 252 253 254 /*************************************************************************/ 255 /*************************************************************************/ 256 /*************************************************************************/ 257 /**** ****/ 258 /**** E D G E H I N T I N G ****/ 259 /**** ****/ 260 /*************************************************************************/ 261 /*************************************************************************/ 262 /*************************************************************************/ 263 264 265 /* Another alternative edge hinting algorithm */ 266 static void ah_hint_edges_3(AH_Hinter hinter)267 ah_hint_edges_3( AH_Hinter hinter ) 268 { 269 AH_Edge edges; 270 AH_Edge edge_limit; 271 AH_Outline outline = hinter->glyph; 272 FT_Int dimension; 273 274 275 edges = outline->horz_edges; 276 edge_limit = edges + outline->num_hedges; 277 278 for ( dimension = 1; dimension >= 0; dimension-- ) 279 { 280 AH_Edge edge; 281 AH_Edge anchor = 0; 282 int has_serifs = 0; 283 284 285 if ( !hinter->do_horz_hints && !dimension ) 286 goto Next_Dimension; 287 288 if ( !hinter->do_vert_hints && dimension ) 289 goto Next_Dimension; 290 291 /* we begin by aligning all stems relative to the blue zone */ 292 /* if needed -- that's only for horizontal edges */ 293 if ( dimension ) 294 { 295 for ( edge = edges; edge < edge_limit; edge++ ) 296 { 297 FT_Pos* blue; 298 AH_EdgeRec *edge1, *edge2; 299 300 301 if ( edge->flags & AH_EDGE_DONE ) 302 continue; 303 304 blue = edge->blue_edge; 305 edge1 = 0; 306 edge2 = edge->link; 307 308 if ( blue ) 309 { 310 edge1 = edge; 311 } 312 else if (edge2 && edge2->blue_edge) 313 { 314 blue = edge2->blue_edge; 315 edge1 = edge2; 316 edge2 = edge; 317 } 318 319 if ( !edge1 ) 320 continue; 321 322 edge1->pos = blue[0]; 323 edge1->flags |= AH_EDGE_DONE; 324 325 if ( edge2 && !edge2->blue_edge ) 326 { 327 ah_align_linked_edge( hinter, edge1, edge2, dimension ); 328 edge2->flags |= AH_EDGE_DONE; 329 } 330 331 if ( !anchor ) 332 anchor = edge; 333 } 334 } 335 336 /* now, we will align all stem edges, trying to maintain the */ 337 /* relative order of stems in the glyph.. */ 338 for ( edge = edges; edge < edge_limit; edge++ ) 339 { 340 AH_EdgeRec* edge2; 341 342 343 if ( edge->flags & AH_EDGE_DONE ) 344 continue; 345 346 /* skip all non-stem edges */ 347 edge2 = edge->link; 348 if ( !edge2 ) 349 { 350 has_serifs++; 351 continue; 352 } 353 354 /* now, align the stem */ 355 356 /* this should not happen, but it's better to be safe. */ 357 if ( edge2->blue_edge || edge2 < edge ) 358 { 359 360 ah_align_linked_edge( hinter, edge2, edge, dimension ); 361 edge->flags |= AH_EDGE_DONE; 362 continue; 363 } 364 365 if ( !anchor ) 366 { 367 edge->pos = ( edge->opos + 32 ) & -64; 368 anchor = edge; 369 370 edge->flags |= AH_EDGE_DONE; 371 372 ah_align_linked_edge( hinter, edge, edge2, dimension ); 373 } 374 else 375 { 376 FT_Pos org_pos, org_len, org_center, cur_len; 377 FT_Pos cur_pos1, cur_pos2, delta1, delta2; 378 379 380 org_pos = anchor->pos + (edge->opos - anchor->opos); 381 org_len = edge2->opos - edge->opos; 382 org_center = org_pos + ( org_len >> 1 ); 383 384 cur_len = ah_compute_stem_width( hinter, dimension, org_len ); 385 386 cur_pos1 = ( org_pos + 32 ) & -64; 387 delta1 = ( cur_pos1 + ( cur_len >> 1 ) - org_center ); 388 if ( delta1 < 0 ) 389 delta1 = -delta1; 390 391 cur_pos2 = ( ( org_pos + org_len + 32 ) & -64 ) - cur_len; 392 delta2 = ( cur_pos2 + ( cur_len >> 1 ) - org_center ); 393 if ( delta2 < 0 ) 394 delta2 = -delta2; 395 396 edge->pos = ( delta1 <= delta2 ) ? cur_pos1 : cur_pos2; 397 edge2->pos = edge->pos + cur_len; 398 399 edge->flags |= AH_EDGE_DONE; 400 edge2->flags |= AH_EDGE_DONE; 401 402 if ( edge > edges && edge->pos < edge[-1].pos ) 403 edge->pos = edge[-1].pos; 404 } 405 } 406 407 if ( !has_serifs ) 408 goto Next_Dimension; 409 410 /* now, hint the remaining edges (serifs and single) in order */ 411 /* to complete our processing */ 412 for ( edge = edges; edge < edge_limit; edge++ ) 413 { 414 if ( edge->flags & AH_EDGE_DONE ) 415 continue; 416 417 if ( edge->serif ) 418 ah_align_serif_edge( hinter, edge->serif, edge, dimension ); 419 else if ( !anchor ) 420 { 421 edge->pos = ( edge->opos + 32 ) & -64; 422 anchor = edge; 423 } 424 else 425 edge->pos = anchor->pos + 426 ( ( edge->opos-anchor->opos + 32 ) & -64 ); 427 428 edge->flags |= AH_EDGE_DONE; 429 430 if ( edge > edges && edge->pos < edge[-1].pos ) 431 edge->pos = edge[-1].pos; 432 433 if ( edge + 1 < edge_limit && 434 edge[1].flags & AH_EDGE_DONE && 435 edge->pos > edge[1].pos ) 436 edge->pos = edge[1].pos; 437 } 438 439 Next_Dimension: 440 edges = outline->vert_edges; 441 edge_limit = edges + outline->num_vedges; 442 } 443 } 444 445 446 FT_LOCAL_DEF( void ) ah_hinter_hint_edges(AH_Hinter hinter)447 ah_hinter_hint_edges( AH_Hinter hinter ) 448 { 449 /* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help */ 450 /* reduce the problem of the disappearing eye in the `e' of Times... */ 451 /* also, creates some artifacts near the blue zones? */ 452 { 453 ah_hint_edges_3( hinter ); 454 } 455 } 456 457 458 /*************************************************************************/ 459 /*************************************************************************/ 460 /*************************************************************************/ 461 /**** ****/ 462 /**** P O I N T H I N T I N G ****/ 463 /**** ****/ 464 /*************************************************************************/ 465 /*************************************************************************/ 466 /*************************************************************************/ 467 468 static void ah_hinter_align_edge_points(AH_Hinter hinter)469 ah_hinter_align_edge_points( AH_Hinter hinter ) 470 { 471 AH_Outline outline = hinter->glyph; 472 AH_Edge edges; 473 AH_Edge edge_limit; 474 FT_Int dimension; 475 476 477 edges = outline->horz_edges; 478 edge_limit = edges + outline->num_hedges; 479 480 for ( dimension = 1; dimension >= 0; dimension-- ) 481 { 482 AH_Edge edge; 483 484 485 edge = edges; 486 for ( ; edge < edge_limit; edge++ ) 487 { 488 /* move the points of each segment */ 489 /* in each edge to the edge's position */ 490 AH_Segment seg = edge->first; 491 492 493 do 494 { 495 AH_Point point = seg->first; 496 497 498 for (;;) 499 { 500 if ( dimension ) 501 { 502 point->y = edge->pos; 503 point->flags |= AH_FLAG_TOUCH_Y; 504 } 505 else 506 { 507 point->x = edge->pos; 508 point->flags |= AH_FLAG_TOUCH_X; 509 } 510 511 if ( point == seg->last ) 512 break; 513 514 point = point->next; 515 } 516 517 seg = seg->edge_next; 518 519 } while ( seg != edge->first ); 520 } 521 522 edges = outline->vert_edges; 523 edge_limit = edges + outline->num_vedges; 524 } 525 } 526 527 528 /* hint the strong points -- this is equivalent to the TrueType `IP' */ 529 static void ah_hinter_align_strong_points(AH_Hinter hinter)530 ah_hinter_align_strong_points( AH_Hinter hinter ) 531 { 532 AH_Outline outline = hinter->glyph; 533 FT_Int dimension; 534 AH_Edge edges; 535 AH_Edge edge_limit; 536 AH_Point points; 537 AH_Point point_limit; 538 AH_Flags touch_flag; 539 540 541 points = outline->points; 542 point_limit = points + outline->num_points; 543 544 edges = outline->horz_edges; 545 edge_limit = edges + outline->num_hedges; 546 touch_flag = AH_FLAG_TOUCH_Y; 547 548 for ( dimension = 1; dimension >= 0; dimension-- ) 549 { 550 AH_Point point; 551 AH_Edge edge; 552 553 554 if ( edges < edge_limit ) 555 for ( point = points; point < point_limit; point++ ) 556 { 557 FT_Pos u, ou, fu; /* point position */ 558 FT_Pos delta; 559 560 561 if ( point->flags & touch_flag ) 562 continue; 563 564 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION 565 /* if this point is candidate to weak interpolation, we will */ 566 /* interpolate it after all strong points have been processed */ 567 if ( ( point->flags & AH_FLAG_WEAK_INTERPOLATION ) && 568 !( point->flags & AH_FLAG_INFLECTION ) ) 569 continue; 570 #endif 571 572 if ( dimension ) 573 { 574 u = point->fy; 575 ou = point->oy; 576 } 577 else 578 { 579 u = point->fx; 580 ou = point->ox; 581 } 582 583 fu = u; 584 585 /* is the point before the first edge? */ 586 edge = edges; 587 delta = edge->fpos - u; 588 if ( delta >= 0 ) 589 { 590 u = edge->pos - ( edge->opos - ou ); 591 goto Store_Point; 592 } 593 594 /* is the point after the last edge ? */ 595 edge = edge_limit - 1; 596 delta = u - edge->fpos; 597 if ( delta >= 0 ) 598 { 599 u = edge->pos + ( ou - edge->opos ); 600 goto Store_Point; 601 } 602 603 /* otherwise, interpolate the point in between */ 604 { 605 AH_Edge before = 0; 606 AH_Edge after = 0; 607 608 609 for ( edge = edges; edge < edge_limit; edge++ ) 610 { 611 if ( u == edge->fpos ) 612 { 613 u = edge->pos; 614 goto Store_Point; 615 } 616 if ( u < edge->fpos ) 617 break; 618 before = edge; 619 } 620 621 for ( edge = edge_limit - 1; edge >= edges; edge-- ) 622 { 623 if ( u == edge->fpos ) 624 { 625 u = edge->pos; 626 goto Store_Point; 627 } 628 if ( u > edge->fpos ) 629 break; 630 after = edge; 631 } 632 633 /* assert( before && after && before != after ) */ 634 u = before->pos + FT_MulDiv( fu - before->fpos, 635 after->pos - before->pos, 636 after->fpos - before->fpos ); 637 } 638 639 Store_Point: 640 641 /* save the point position */ 642 if ( dimension ) 643 point->y = u; 644 else 645 point->x = u; 646 647 point->flags |= touch_flag; 648 } 649 650 edges = outline->vert_edges; 651 edge_limit = edges + outline->num_vedges; 652 touch_flag = AH_FLAG_TOUCH_X; 653 } 654 } 655 656 657 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION 658 659 static void ah_iup_shift(AH_Point p1,AH_Point p2,AH_Point ref)660 ah_iup_shift( AH_Point p1, 661 AH_Point p2, 662 AH_Point ref ) 663 { 664 AH_Point p; 665 FT_Pos delta = ref->u - ref->v; 666 667 668 for ( p = p1; p < ref; p++ ) 669 p->u = p->v + delta; 670 671 for ( p = ref + 1; p <= p2; p++ ) 672 p->u = p->v + delta; 673 } 674 675 676 static void ah_iup_interp(AH_Point p1,AH_Point p2,AH_Point ref1,AH_Point ref2)677 ah_iup_interp( AH_Point p1, 678 AH_Point p2, 679 AH_Point ref1, 680 AH_Point ref2 ) 681 { 682 AH_Point p; 683 FT_Pos u; 684 FT_Pos v1 = ref1->v; 685 FT_Pos v2 = ref2->v; 686 FT_Pos d1 = ref1->u - v1; 687 FT_Pos d2 = ref2->u - v2; 688 689 690 if ( p1 > p2 ) 691 return; 692 693 if ( v1 == v2 ) 694 { 695 for ( p = p1; p <= p2; p++ ) 696 { 697 u = p->v; 698 699 if ( u <= v1 ) 700 u += d1; 701 else 702 u += d2; 703 704 p->u = u; 705 } 706 return; 707 } 708 709 if ( v1 < v2 ) 710 { 711 for ( p = p1; p <= p2; p++ ) 712 { 713 u = p->v; 714 715 if ( u <= v1 ) 716 u += d1; 717 else if ( u >= v2 ) 718 u += d2; 719 else 720 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); 721 722 p->u = u; 723 } 724 } 725 else 726 { 727 for ( p = p1; p <= p2; p++ ) 728 { 729 u = p->v; 730 731 if ( u <= v2 ) 732 u += d2; 733 else if ( u >= v1 ) 734 u += d1; 735 else 736 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); 737 738 p->u = u; 739 } 740 } 741 } 742 743 744 /* interpolate weak points -- this is equivalent to the TrueType `IUP' */ 745 static void ah_hinter_align_weak_points(AH_Hinter hinter)746 ah_hinter_align_weak_points( AH_Hinter hinter ) 747 { 748 AH_Outline outline = hinter->glyph; 749 FT_Int dimension; 750 AH_Point points; 751 AH_Point point_limit; 752 AH_Point* contour_limit; 753 AH_Flags touch_flag; 754 755 756 points = outline->points; 757 point_limit = points + outline->num_points; 758 759 /* PASS 1: Move segment points to edge positions */ 760 761 touch_flag = AH_FLAG_TOUCH_Y; 762 763 contour_limit = outline->contours + outline->num_contours; 764 765 ah_setup_uv( outline, AH_UV_OY ); 766 767 for ( dimension = 1; dimension >= 0; dimension-- ) 768 { 769 AH_Point point; 770 AH_Point end_point; 771 AH_Point first_point; 772 AH_Point* contour; 773 774 775 point = points; 776 contour = outline->contours; 777 778 for ( ; contour < contour_limit; contour++ ) 779 { 780 point = *contour; 781 end_point = point->prev; 782 first_point = point; 783 784 while ( point <= end_point && !( point->flags & touch_flag ) ) 785 point++; 786 787 if ( point <= end_point ) 788 { 789 AH_Point first_touched = point; 790 AH_Point cur_touched = point; 791 792 793 point++; 794 while ( point <= end_point ) 795 { 796 if ( point->flags & touch_flag ) 797 { 798 /* we found two successive touched points; we interpolate */ 799 /* all contour points between them */ 800 ah_iup_interp( cur_touched + 1, point - 1, 801 cur_touched, point ); 802 cur_touched = point; 803 } 804 point++; 805 } 806 807 if ( cur_touched == first_touched ) 808 { 809 /* this is a special case: only one point was touched in the */ 810 /* contour; we thus simply shift the whole contour */ 811 ah_iup_shift( first_point, end_point, cur_touched ); 812 } 813 else 814 { 815 /* now interpolate after the last touched point to the end */ 816 /* of the contour */ 817 ah_iup_interp( cur_touched + 1, end_point, 818 cur_touched, first_touched ); 819 820 /* if the first contour point isn't touched, interpolate */ 821 /* from the contour start to the first touched point */ 822 if ( first_touched > points ) 823 ah_iup_interp( first_point, first_touched - 1, 824 cur_touched, first_touched ); 825 } 826 } 827 } 828 829 /* now save the interpolated values back to x/y */ 830 if ( dimension ) 831 { 832 for ( point = points; point < point_limit; point++ ) 833 point->y = point->u; 834 835 touch_flag = AH_FLAG_TOUCH_X; 836 ah_setup_uv( outline, AH_UV_OX ); 837 } 838 else 839 { 840 for ( point = points; point < point_limit; point++ ) 841 point->x = point->u; 842 843 break; /* exit loop */ 844 } 845 } 846 } 847 848 #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */ 849 850 851 FT_LOCAL_DEF( void ) ah_hinter_align_points(AH_Hinter hinter)852 ah_hinter_align_points( AH_Hinter hinter ) 853 { 854 ah_hinter_align_edge_points( hinter ); 855 856 #ifndef AH_OPTION_NO_STRONG_INTERPOLATION 857 ah_hinter_align_strong_points( hinter ); 858 #endif 859 860 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION 861 ah_hinter_align_weak_points( hinter ); 862 #endif 863 } 864 865 866 /*************************************************************************/ 867 /*************************************************************************/ 868 /*************************************************************************/ 869 /**** ****/ 870 /**** H I N T E R O B J E C T M E T H O D S ****/ 871 /**** ****/ 872 /*************************************************************************/ 873 /*************************************************************************/ 874 /*************************************************************************/ 875 876 877 /* scale and fit the global metrics */ 878 static void ah_hinter_scale_globals(AH_Hinter hinter,FT_Fixed x_scale,FT_Fixed y_scale)879 ah_hinter_scale_globals( AH_Hinter hinter, 880 FT_Fixed x_scale, 881 FT_Fixed y_scale ) 882 { 883 FT_Int n; 884 AH_Face_Globals globals = hinter->globals; 885 AH_Globals design = &globals->design; 886 AH_Globals scaled = &globals->scaled; 887 888 889 /* copy content */ 890 *scaled = *design; 891 892 /* scale the standard widths & heights */ 893 for ( n = 0; n < design->num_widths; n++ ) 894 scaled->widths[n] = FT_MulFix( design->widths[n], x_scale ); 895 896 for ( n = 0; n < design->num_heights; n++ ) 897 scaled->heights[n] = FT_MulFix( design->heights[n], y_scale ); 898 899 scaled->stds[0] = ( design->num_widths > 0 ) ? scaled->widths[0] : 32000; 900 scaled->stds[1] = ( design->num_heights > 0 ) ? scaled->heights[0] : 32000; 901 902 /* scale the blue zones */ 903 for ( n = 0; n < AH_BLUE_MAX; n++ ) 904 { 905 FT_Pos delta, delta2; 906 907 908 delta = design->blue_shoots[n] - design->blue_refs[n]; 909 delta2 = delta; 910 if ( delta < 0 ) 911 delta2 = -delta2; 912 delta2 = FT_MulFix( delta2, y_scale ); 913 914 if ( delta2 < 32 ) 915 delta2 = 0; 916 else if ( delta2 < 64 ) 917 delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & -32 ); 918 else 919 delta2 = ( delta2 + 32 ) & -64; 920 921 if ( delta < 0 ) 922 delta2 = -delta2; 923 924 scaled->blue_refs[n] = 925 ( FT_MulFix( design->blue_refs[n], y_scale ) + 32 ) & -64; 926 scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2; 927 } 928 929 globals->x_scale = x_scale; 930 globals->y_scale = y_scale; 931 } 932 933 934 static void ah_hinter_align(AH_Hinter hinter)935 ah_hinter_align( AH_Hinter hinter ) 936 { 937 ah_hinter_align_edge_points( hinter ); 938 ah_hinter_align_points( hinter ); 939 } 940 941 942 /* finalize a hinter object */ 943 FT_LOCAL_DEF( void ) ah_hinter_done(AH_Hinter hinter)944 ah_hinter_done( AH_Hinter hinter ) 945 { 946 if ( hinter ) 947 { 948 FT_Memory memory = hinter->memory; 949 950 951 ah_loader_done( hinter->loader ); 952 ah_outline_done( hinter->glyph ); 953 954 /* note: the `globals' pointer is _not_ owned by the hinter */ 955 /* but by the current face object, we don't need to */ 956 /* release it */ 957 hinter->globals = 0; 958 hinter->face = 0; 959 960 FT_FREE( hinter ); 961 } 962 } 963 964 965 /* create a new empty hinter object */ 966 FT_LOCAL_DEF( FT_Error ) ah_hinter_new(FT_Library library,AH_Hinter * ahinter)967 ah_hinter_new( FT_Library library, 968 AH_Hinter *ahinter ) 969 { 970 AH_Hinter hinter = 0; 971 FT_Memory memory = library->memory; 972 FT_Error error; 973 974 975 *ahinter = 0; 976 977 /* allocate object */ 978 if ( FT_NEW( hinter ) ) 979 goto Exit; 980 981 hinter->memory = memory; 982 hinter->flags = 0; 983 984 /* allocate outline and loader */ 985 error = ah_outline_new( memory, &hinter->glyph ) || 986 ah_loader_new ( memory, &hinter->loader ) || 987 ah_loader_create_extra( hinter->loader ); 988 if ( error ) 989 goto Exit; 990 991 *ahinter = hinter; 992 993 Exit: 994 if ( error ) 995 ah_hinter_done( hinter ); 996 997 return error; 998 } 999 1000 1001 /* create a face's autohint globals */ 1002 FT_LOCAL_DEF( FT_Error ) ah_hinter_new_face_globals(AH_Hinter hinter,FT_Face face,AH_Globals globals)1003 ah_hinter_new_face_globals( AH_Hinter hinter, 1004 FT_Face face, 1005 AH_Globals globals ) 1006 { 1007 FT_Error error; 1008 FT_Memory memory = hinter->memory; 1009 AH_Face_Globals face_globals; 1010 1011 1012 if ( FT_NEW( face_globals ) ) 1013 goto Exit; 1014 1015 hinter->face = face; 1016 hinter->globals = face_globals; 1017 1018 if ( globals ) 1019 face_globals->design = *globals; 1020 else 1021 ah_hinter_compute_globals( hinter ); 1022 1023 face->autohint.data = face_globals; 1024 face->autohint.finalizer = (FT_Generic_Finalizer) 1025 ah_hinter_done_face_globals; 1026 face_globals->face = face; 1027 1028 Exit: 1029 return error; 1030 } 1031 1032 1033 /* discard a face's autohint globals */ 1034 FT_LOCAL_DEF( void ) ah_hinter_done_face_globals(AH_Face_Globals globals)1035 ah_hinter_done_face_globals( AH_Face_Globals globals ) 1036 { 1037 FT_Face face = globals->face; 1038 FT_Memory memory = face->memory; 1039 1040 1041 FT_FREE( globals ); 1042 } 1043 1044 1045 static FT_Error ah_hinter_load(AH_Hinter hinter,FT_UInt glyph_index,FT_Int32 load_flags,FT_UInt depth)1046 ah_hinter_load( AH_Hinter hinter, 1047 FT_UInt glyph_index, 1048 FT_Int32 load_flags, 1049 FT_UInt depth ) 1050 { 1051 FT_Face face = hinter->face; 1052 FT_GlyphSlot slot = face->glyph; 1053 FT_Slot_Internal internal = slot->internal; 1054 FT_Fixed x_scale = face->size->metrics.x_scale; 1055 FT_Fixed y_scale = face->size->metrics.y_scale; 1056 FT_Error error; 1057 AH_Outline outline = hinter->glyph; 1058 AH_Loader gloader = hinter->loader; 1059 1060 1061 /* load the glyph */ 1062 error = FT_Load_Glyph( face, glyph_index, load_flags ); 1063 if ( error ) 1064 goto Exit; 1065 1066 /* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */ 1067 hinter->transformed = internal->glyph_transformed; 1068 1069 if ( hinter->transformed ) 1070 { 1071 FT_Matrix imatrix; 1072 1073 1074 imatrix = internal->glyph_matrix; 1075 hinter->trans_delta = internal->glyph_delta; 1076 hinter->trans_matrix = imatrix; 1077 1078 FT_Matrix_Invert( &imatrix ); 1079 FT_Vector_Transform( &hinter->trans_delta, &imatrix ); 1080 } 1081 1082 /* set linear horizontal metrics */ 1083 slot->linearHoriAdvance = slot->metrics.horiAdvance; 1084 slot->linearVertAdvance = slot->metrics.vertAdvance; 1085 1086 switch ( slot->format ) 1087 { 1088 case FT_GLYPH_FORMAT_OUTLINE: 1089 1090 /* translate glyph outline if we need to */ 1091 if ( hinter->transformed ) 1092 { 1093 FT_UInt n = slot->outline.n_points; 1094 FT_Vector* point = slot->outline.points; 1095 1096 1097 for ( ; n > 0; point++, n-- ) 1098 { 1099 point->x += hinter->trans_delta.x; 1100 point->y += hinter->trans_delta.y; 1101 } 1102 } 1103 1104 /* copy the outline points in the loader's current */ 1105 /* extra points, which is used to keep original glyph coordinates */ 1106 error = ah_loader_check_points( gloader, slot->outline.n_points + 2, 1107 slot->outline.n_contours ); 1108 if ( error ) 1109 goto Exit; 1110 1111 FT_MEM_COPY( gloader->current.extra_points, slot->outline.points, 1112 slot->outline.n_points * sizeof ( FT_Vector ) ); 1113 1114 FT_MEM_COPY( gloader->current.outline.contours, slot->outline.contours, 1115 slot->outline.n_contours * sizeof ( short ) ); 1116 1117 FT_MEM_COPY( gloader->current.outline.tags, slot->outline.tags, 1118 slot->outline.n_points * sizeof ( char ) ); 1119 1120 gloader->current.outline.n_points = slot->outline.n_points; 1121 gloader->current.outline.n_contours = slot->outline.n_contours; 1122 1123 /* compute original phantom points */ 1124 hinter->pp1.x = 0; 1125 hinter->pp1.y = 0; 1126 hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale ); 1127 hinter->pp2.y = 0; 1128 1129 /* be sure to check for spacing glyphs */ 1130 if ( slot->outline.n_points == 0 ) 1131 goto Hint_Metrics; 1132 1133 /* now, load the slot image into the auto-outline, and run the */ 1134 /* automatic hinting process */ 1135 error = ah_outline_load( outline, face ); /* XXX: change to slot */ 1136 if ( error ) 1137 goto Exit; 1138 1139 /* perform feature detection */ 1140 ah_outline_detect_features( outline ); 1141 1142 if ( hinter->do_vert_hints ) 1143 { 1144 ah_outline_compute_blue_edges( outline, hinter->globals ); 1145 ah_outline_scale_blue_edges( outline, hinter->globals ); 1146 } 1147 1148 /* perform alignment control */ 1149 ah_hinter_hint_edges( hinter ); 1150 ah_hinter_align( hinter ); 1151 1152 /* now save the current outline into the loader's current table */ 1153 ah_outline_save( outline, gloader ); 1154 1155 /* we now need to hint the metrics according to the change in */ 1156 /* width/positioning that occured during the hinting process */ 1157 { 1158 FT_Pos old_advance, old_rsb, old_lsb, new_lsb; 1159 AH_Edge edge1 = outline->vert_edges; /* leftmost edge */ 1160 AH_Edge edge2 = edge1 + 1161 outline->num_vedges - 1; /* rightmost edge */ 1162 1163 1164 old_advance = hinter->pp2.x; 1165 old_rsb = old_advance - edge2->opos; 1166 old_lsb = edge1->opos; 1167 new_lsb = edge1->pos; 1168 1169 hinter->pp1.x = ( ( new_lsb - old_lsb ) + 32 ) & -64; 1170 hinter->pp2.x = ( ( edge2->pos + old_rsb ) + 32 ) & -64; 1171 1172 /* try to fix certain bad advance computations */ 1173 if ( hinter->pp2.x + hinter->pp1.x == edge2->pos && old_rsb > 4 ) 1174 hinter->pp2.x += 64; 1175 } 1176 1177 /* good, we simply add the glyph to our loader's base */ 1178 ah_loader_add( gloader ); 1179 break; 1180 1181 case FT_GLYPH_FORMAT_COMPOSITE: 1182 { 1183 FT_UInt nn, num_subglyphs = slot->num_subglyphs; 1184 FT_UInt num_base_subgs, start_point; 1185 FT_SubGlyph subglyph; 1186 1187 1188 start_point = gloader->base.outline.n_points; 1189 1190 /* first of all, copy the subglyph descriptors in the glyph loader */ 1191 error = ah_loader_check_subglyphs( gloader, num_subglyphs ); 1192 if ( error ) 1193 goto Exit; 1194 1195 FT_MEM_COPY( gloader->current.subglyphs, slot->subglyphs, 1196 num_subglyphs * sizeof ( FT_SubGlyph ) ); 1197 1198 gloader->current.num_subglyphs = num_subglyphs; 1199 num_base_subgs = gloader->base.num_subglyphs; 1200 1201 /* now, read each subglyph independently */ 1202 for ( nn = 0; nn < num_subglyphs; nn++ ) 1203 { 1204 FT_Vector pp1, pp2; 1205 FT_Pos x, y; 1206 FT_UInt num_points, num_new_points, num_base_points; 1207 1208 1209 /* gloader.current.subglyphs can change during glyph loading due */ 1210 /* to re-allocation -- we must recompute the current subglyph on */ 1211 /* each iteration */ 1212 subglyph = gloader->base.subglyphs + num_base_subgs + nn; 1213 1214 pp1 = hinter->pp1; 1215 pp2 = hinter->pp2; 1216 1217 num_base_points = gloader->base.outline.n_points; 1218 1219 error = ah_hinter_load( hinter, subglyph->index, 1220 load_flags, depth + 1 ); 1221 if ( error ) 1222 goto Exit; 1223 1224 /* recompute subglyph pointer */ 1225 subglyph = gloader->base.subglyphs + num_base_subgs + nn; 1226 1227 if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS ) 1228 { 1229 pp1 = hinter->pp1; 1230 pp2 = hinter->pp2; 1231 } 1232 else 1233 { 1234 hinter->pp1 = pp1; 1235 hinter->pp2 = pp2; 1236 } 1237 1238 num_points = gloader->base.outline.n_points; 1239 num_new_points = num_points - num_base_points; 1240 1241 /* now perform the transform required for this subglyph */ 1242 1243 if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE | 1244 FT_SUBGLYPH_FLAG_XY_SCALE | 1245 FT_SUBGLYPH_FLAG_2X2 ) ) 1246 { 1247 FT_Vector* cur = gloader->base.outline.points + 1248 num_base_points; 1249 FT_Vector* org = gloader->base.extra_points + 1250 num_base_points; 1251 FT_Vector* limit = cur + num_new_points; 1252 1253 1254 for ( ; cur < limit; cur++, org++ ) 1255 { 1256 FT_Vector_Transform( cur, &subglyph->transform ); 1257 FT_Vector_Transform( org, &subglyph->transform ); 1258 } 1259 } 1260 1261 /* apply offset */ 1262 1263 if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) ) 1264 { 1265 FT_Int k = subglyph->arg1; 1266 FT_UInt l = subglyph->arg2; 1267 FT_Vector* p1; 1268 FT_Vector* p2; 1269 1270 1271 if ( start_point + k >= num_base_points || 1272 l >= (FT_UInt)num_new_points ) 1273 { 1274 error = AH_Err_Invalid_Composite; 1275 goto Exit; 1276 } 1277 1278 l += num_base_points; 1279 1280 /* for now, only use the current point coordinates */ 1281 /* we may consider another approach in the near future */ 1282 p1 = gloader->base.outline.points + start_point + k; 1283 p2 = gloader->base.outline.points + start_point + l; 1284 1285 x = p1->x - p2->x; 1286 y = p1->y - p2->y; 1287 } 1288 else 1289 { 1290 x = FT_MulFix( subglyph->arg1, x_scale ); 1291 y = FT_MulFix( subglyph->arg2, y_scale ); 1292 1293 x = ( x + 32 ) & -64; 1294 y = ( y + 32 ) & -64; 1295 } 1296 1297 { 1298 FT_Outline dummy = gloader->base.outline; 1299 1300 1301 dummy.points += num_base_points; 1302 dummy.n_points = (short)num_new_points; 1303 1304 FT_Outline_Translate( &dummy, x, y ); 1305 } 1306 } 1307 } 1308 break; 1309 1310 default: 1311 /* we don't support other formats (yet?) */ 1312 error = AH_Err_Unimplemented_Feature; 1313 } 1314 1315 Hint_Metrics: 1316 if ( depth == 0 ) 1317 { 1318 FT_BBox bbox; 1319 1320 1321 /* transform the hinted outline if needed */ 1322 if ( hinter->transformed ) 1323 FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix ); 1324 1325 /* we must translate our final outline by -pp1.x, and compute */ 1326 /* the new metrics */ 1327 if ( hinter->pp1.x ) 1328 FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 ); 1329 1330 FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); 1331 bbox.xMin &= -64; 1332 bbox.yMin &= -64; 1333 bbox.xMax = ( bbox.xMax + 63 ) & -64; 1334 bbox.yMax = ( bbox.yMax + 63 ) & -64; 1335 1336 slot->metrics.width = bbox.xMax - bbox.xMin; 1337 slot->metrics.height = bbox.yMax - bbox.yMin; 1338 slot->metrics.horiBearingX = bbox.xMin; 1339 slot->metrics.horiBearingY = bbox.yMax; 1340 1341 /* for mono-width fonts (like Andale, Courier, etc.), we need */ 1342 /* to keep the original rounded advance width */ 1343 if ( !FT_IS_FIXED_WIDTH( slot->face ) ) 1344 slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x; 1345 else 1346 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, 1347 x_scale ); 1348 1349 slot->metrics.horiAdvance = ( slot->metrics.horiAdvance + 32 ) & -64; 1350 1351 /* now copy outline into glyph slot */ 1352 ah_loader_rewind( slot->internal->loader ); 1353 error = ah_loader_copy_points( slot->internal->loader, gloader ); 1354 if ( error ) 1355 goto Exit; 1356 1357 slot->outline = slot->internal->loader->base.outline; 1358 slot->format = FT_GLYPH_FORMAT_OUTLINE; 1359 } 1360 1361 #ifdef DEBUG_HINTER 1362 ah_debug_hinter = hinter; 1363 #endif 1364 1365 Exit: 1366 return error; 1367 } 1368 1369 1370 /* load and hint a given glyph */ 1371 FT_LOCAL_DEF( FT_Error ) ah_hinter_load_glyph(AH_Hinter hinter,FT_GlyphSlot slot,FT_Size size,FT_UInt glyph_index,FT_Int32 load_flags)1372 ah_hinter_load_glyph( AH_Hinter hinter, 1373 FT_GlyphSlot slot, 1374 FT_Size size, 1375 FT_UInt glyph_index, 1376 FT_Int32 load_flags ) 1377 { 1378 FT_Face face = slot->face; 1379 FT_Error error; 1380 FT_Fixed x_scale = size->metrics.x_scale; 1381 FT_Fixed y_scale = size->metrics.y_scale; 1382 AH_Face_Globals face_globals = FACE_GLOBALS( face ); 1383 FT_Render_Mode hint_mode = FT_LOAD_TARGET_MODE(load_flags); 1384 1385 1386 /* first of all, we need to check that we're using the correct face and */ 1387 /* global hints to load the glyph */ 1388 if ( hinter->face != face || hinter->globals != face_globals ) 1389 { 1390 hinter->face = face; 1391 if ( !face_globals ) 1392 { 1393 error = ah_hinter_new_face_globals( hinter, face, 0 ); 1394 if ( error ) 1395 goto Exit; 1396 1397 } 1398 hinter->globals = FACE_GLOBALS( face ); 1399 face_globals = FACE_GLOBALS( face ); 1400 1401 } 1402 1403 /* now, we must check the current character pixel size to see if we */ 1404 /* need to rescale the global metrics */ 1405 if ( face_globals->x_scale != x_scale || 1406 face_globals->y_scale != y_scale ) 1407 ah_hinter_scale_globals( hinter, x_scale, y_scale ); 1408 1409 ah_loader_rewind( hinter->loader ); 1410 1411 /* reset hinting flags according to load flags and current render target */ 1412 hinter->do_horz_hints = !FT_BOOL( load_flags & FT_LOAD_NO_AUTOHINT ); 1413 hinter->do_vert_hints = !FT_BOOL( load_flags & FT_LOAD_NO_AUTOHINT ); 1414 1415 #ifdef DEBUG_HINTER 1416 hinter->do_horz_hints = !ah_debug_disable_vert; /* not a bug, the meaning */ 1417 hinter->do_vert_hints = !ah_debug_disable_horz; /* of h/v is inverted! */ 1418 #endif 1419 1420 /* we snap the width of vertical stems for the monochrome and */ 1421 /* horizontal LCD rendering targets only. Corresponds to X snapping. */ 1422 hinter->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || 1423 hint_mode == FT_RENDER_MODE_LCD ); 1424 1425 /* we snap the width of horizontal stems for the monochrome and */ 1426 /* vertical LCD rendering targets only. Corresponds to Y snapping. */ 1427 hinter->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || 1428 hint_mode == FT_RENDER_MODE_LCD_V ); 1429 1430 #if 1 1431 load_flags = FT_LOAD_NO_SCALE 1432 | FT_LOAD_IGNORE_TRANSFORM ; 1433 #else 1434 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE; 1435 #endif 1436 1437 error = ah_hinter_load( hinter, glyph_index, load_flags, 0 ); 1438 1439 Exit: 1440 return error; 1441 } 1442 1443 1444 /* retrieve a face's autohint globals for client applications */ 1445 FT_LOCAL_DEF( void ) ah_hinter_get_global_hints(AH_Hinter hinter,FT_Face face,void ** global_hints,long * global_len)1446 ah_hinter_get_global_hints( AH_Hinter hinter, 1447 FT_Face face, 1448 void** global_hints, 1449 long* global_len ) 1450 { 1451 AH_Globals globals = 0; 1452 FT_Memory memory = hinter->memory; 1453 FT_Error error; 1454 1455 1456 /* allocate new master globals */ 1457 if ( FT_NEW( globals ) ) 1458 goto Fail; 1459 1460 /* compute face globals if needed */ 1461 if ( !FACE_GLOBALS( face ) ) 1462 { 1463 error = ah_hinter_new_face_globals( hinter, face, 0 ); 1464 if ( error ) 1465 goto Fail; 1466 } 1467 1468 *globals = FACE_GLOBALS( face )->design; 1469 *global_hints = globals; 1470 *global_len = sizeof( *globals ); 1471 1472 return; 1473 1474 Fail: 1475 FT_FREE( globals ); 1476 1477 *global_hints = 0; 1478 *global_len = 0; 1479 } 1480 1481 1482 FT_LOCAL_DEF( void ) ah_hinter_done_global_hints(AH_Hinter hinter,void * global_hints)1483 ah_hinter_done_global_hints( AH_Hinter hinter, 1484 void* global_hints ) 1485 { 1486 FT_Memory memory = hinter->memory; 1487 1488 1489 FT_FREE( global_hints ); 1490 } 1491 1492 1493 /* END */ 1494