afhints.c (47927B)
1 /**************************************************************************** 2 * 3 * afhints.c 4 * 5 * Auto-fitter hinting routines (body). 6 * 7 * Copyright (C) 2003-2025 by 8 * David Turner, Robert Wilhelm, and Werner Lemberg. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 19 #include "afhints.h" 20 #include "aferrors.h" 21 #include <freetype/internal/ftcalc.h> 22 #include <freetype/internal/ftdebug.h> 23 24 25 /************************************************************************** 26 * 27 * The macro FT_COMPONENT is used in trace mode. It is an implicit 28 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 29 * messages during execution. 30 */ 31 #undef FT_COMPONENT 32 #define FT_COMPONENT afhints 33 34 35 FT_LOCAL_DEF( void ) 36 af_sort_pos( FT_UInt count, 37 FT_Pos* table ) 38 { 39 FT_UInt i, j; 40 FT_Pos swap; 41 42 43 for ( i = 1; i < count; i++ ) 44 { 45 for ( j = i; j > 0; j-- ) 46 { 47 if ( table[j] >= table[j - 1] ) 48 break; 49 50 swap = table[j]; 51 table[j] = table[j - 1]; 52 table[j - 1] = swap; 53 } 54 } 55 } 56 57 58 FT_LOCAL_DEF( void ) 59 af_sort_and_quantize_widths( FT_UInt* count, 60 AF_Width table, 61 FT_Pos threshold ) 62 { 63 FT_UInt i, j; 64 FT_UInt cur_idx; 65 FT_Pos cur_val; 66 FT_Pos sum; 67 AF_WidthRec swap; 68 69 70 if ( *count == 1 ) 71 return; 72 73 /* sort */ 74 for ( i = 1; i < *count; i++ ) 75 { 76 for ( j = i; j > 0; j-- ) 77 { 78 if ( table[j].org >= table[j - 1].org ) 79 break; 80 81 swap = table[j]; 82 table[j] = table[j - 1]; 83 table[j - 1] = swap; 84 } 85 } 86 87 cur_idx = 0; 88 cur_val = table[cur_idx].org; 89 90 /* compute and use mean values for clusters not larger than */ 91 /* `threshold'; this is very primitive and might not yield */ 92 /* the best result, but normally, using reference character */ 93 /* `o', `*count' is 2, so the code below is fully sufficient */ 94 for ( i = 1; i < *count; i++ ) 95 { 96 if ( table[i].org - cur_val > threshold || 97 i == *count - 1 ) 98 { 99 sum = 0; 100 101 /* fix loop for end of array */ 102 if ( table[i].org - cur_val <= threshold && 103 i == *count - 1 ) 104 i++; 105 106 for ( j = cur_idx; j < i; j++ ) 107 { 108 sum += table[j].org; 109 table[j].org = 0; 110 } 111 table[cur_idx].org = sum / (FT_Pos)j; 112 113 if ( i < *count - 1 ) 114 { 115 cur_idx = i + 1; 116 cur_val = table[cur_idx].org; 117 } 118 } 119 } 120 121 cur_idx = 1; 122 123 /* compress array to remove zero values */ 124 for ( i = 1; i < *count; i++ ) 125 { 126 if ( table[i].org ) 127 table[cur_idx++] = table[i]; 128 } 129 130 *count = cur_idx; 131 } 132 133 /* Get new segment for given axis. */ 134 135 FT_LOCAL_DEF( FT_Error ) 136 af_axis_hints_new_segment( AF_AxisHints axis, 137 FT_Memory memory, 138 AF_Segment *asegment ) 139 { 140 FT_Error error = FT_Err_Ok; 141 AF_Segment segment = NULL; 142 143 144 if ( axis->num_segments < AF_SEGMENTS_EMBEDDED ) 145 { 146 if ( !axis->segments ) 147 { 148 axis->segments = axis->embedded.segments; 149 axis->max_segments = AF_SEGMENTS_EMBEDDED; 150 } 151 } 152 else if ( axis->num_segments >= axis->max_segments ) 153 { 154 FT_UInt old_max = axis->max_segments; 155 FT_UInt new_max = old_max; 156 FT_UInt big_max = FT_INT_MAX / sizeof ( *segment ); 157 158 159 if ( old_max >= big_max ) 160 { 161 error = FT_THROW( Out_Of_Memory ); 162 goto Exit; 163 } 164 165 new_max += ( new_max >> 2 ) + 4; 166 if ( new_max < old_max || new_max > big_max ) 167 new_max = big_max; 168 169 if ( axis->segments == axis->embedded.segments ) 170 { 171 if ( FT_NEW_ARRAY( axis->segments, new_max ) ) 172 goto Exit; 173 ft_memcpy( axis->segments, axis->embedded.segments, 174 sizeof ( axis->embedded.segments ) ); 175 } 176 else 177 { 178 if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) ) 179 goto Exit; 180 } 181 182 axis->max_segments = new_max; 183 } 184 185 segment = axis->segments + axis->num_segments++; 186 187 Exit: 188 *asegment = segment; 189 return error; 190 } 191 192 193 /* Get new edge for given axis, direction, and position, */ 194 /* without initializing the edge itself. */ 195 196 FT_LOCAL_DEF( FT_Error ) 197 af_axis_hints_new_edge( AF_AxisHints axis, 198 FT_Int fpos, 199 AF_Direction dir, 200 FT_Bool top_to_bottom_hinting, 201 FT_Memory memory, 202 AF_Edge *anedge ) 203 { 204 FT_Error error = FT_Err_Ok; 205 AF_Edge edge = NULL; 206 AF_Edge edges; 207 208 209 if ( axis->num_edges < AF_EDGES_EMBEDDED ) 210 { 211 if ( !axis->edges ) 212 { 213 axis->edges = axis->embedded.edges; 214 axis->max_edges = AF_EDGES_EMBEDDED; 215 } 216 } 217 else if ( axis->num_edges >= axis->max_edges ) 218 { 219 FT_UInt old_max = axis->max_edges; 220 FT_UInt new_max = old_max; 221 FT_UInt big_max = FT_INT_MAX / sizeof ( *edge ); 222 223 224 if ( old_max >= big_max ) 225 { 226 error = FT_THROW( Out_Of_Memory ); 227 goto Exit; 228 } 229 230 new_max += ( new_max >> 2 ) + 4; 231 if ( new_max < old_max || new_max > big_max ) 232 new_max = big_max; 233 234 if ( axis->edges == axis->embedded.edges ) 235 { 236 if ( FT_NEW_ARRAY( axis->edges, new_max ) ) 237 goto Exit; 238 ft_memcpy( axis->edges, axis->embedded.edges, 239 sizeof ( axis->embedded.edges ) ); 240 } 241 else 242 { 243 if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) ) 244 goto Exit; 245 } 246 247 axis->max_edges = new_max; 248 } 249 250 edges = axis->edges; 251 edge = edges + axis->num_edges; 252 253 while ( edge > edges ) 254 { 255 if ( top_to_bottom_hinting ? ( edge[-1].fpos > fpos ) 256 : ( edge[-1].fpos < fpos ) ) 257 break; 258 259 /* we want the edge with same position and minor direction */ 260 /* to appear before those in the major one in the list */ 261 if ( edge[-1].fpos == fpos && dir == axis->major_dir ) 262 break; 263 264 edge[0] = edge[-1]; 265 edge--; 266 } 267 268 axis->num_edges++; 269 270 Exit: 271 *anedge = edge; 272 return error; 273 } 274 275 276 #ifdef FT_DEBUG_AUTOFIT 277 278 #include FT_CONFIG_STANDARD_LIBRARY_H 279 280 /* The dump functions are used in the `ftgrid' demo program, too. */ 281 #define AF_DUMP( varformat ) \ 282 do \ 283 { \ 284 if ( to_stdout ) \ 285 printf varformat; \ 286 else \ 287 FT_TRACE7( varformat ); \ 288 } while ( 0 ) 289 290 291 static const char* 292 af_dir_str( AF_Direction dir ) 293 { 294 const char* result; 295 296 297 switch ( dir ) 298 { 299 case AF_DIR_UP: 300 result = "up"; 301 break; 302 case AF_DIR_DOWN: 303 result = "down"; 304 break; 305 case AF_DIR_LEFT: 306 result = "left"; 307 break; 308 case AF_DIR_RIGHT: 309 result = "right"; 310 break; 311 default: 312 result = "none"; 313 } 314 315 return result; 316 } 317 318 319 #define AF_INDEX_NUM( ptr, base ) (int)( (ptr) ? ( (ptr) - (base) ) : -1 ) 320 321 322 static char* 323 af_print_idx( char* p, 324 size_t n, 325 int idx ) 326 { 327 if ( idx == -1 ) 328 { 329 p[0] = '-'; 330 p[1] = '-'; 331 p[2] = '\0'; 332 } 333 else 334 ft_snprintf( p, n, "%d", idx ); 335 336 return p; 337 } 338 339 340 static int 341 af_get_segment_index( AF_GlyphHints hints, 342 int point_idx, 343 int dimension ) 344 { 345 AF_AxisHints axis = &hints->axis[dimension]; 346 AF_Point point = hints->points + point_idx; 347 AF_Segment segments = axis->segments; 348 AF_Segment limit = segments + axis->num_segments; 349 AF_Segment segment; 350 351 352 for ( segment = segments; segment < limit; segment++ ) 353 { 354 if ( segment->first <= segment->last ) 355 { 356 if ( point >= segment->first && point <= segment->last ) 357 break; 358 } 359 else 360 { 361 AF_Point p = segment->first; 362 363 364 for (;;) 365 { 366 if ( point == p ) 367 goto Exit; 368 369 if ( p == segment->last ) 370 break; 371 372 p = p->next; 373 } 374 } 375 } 376 377 Exit: 378 if ( segment == limit ) 379 return -1; 380 381 return (int)( segment - segments ); 382 } 383 384 385 static int 386 af_get_edge_index( AF_GlyphHints hints, 387 int segment_idx, 388 int dimension ) 389 { 390 AF_AxisHints axis = &hints->axis[dimension]; 391 AF_Edge edges = axis->edges; 392 AF_Segment segment = axis->segments + segment_idx; 393 394 395 return segment_idx == -1 ? -1 : AF_INDEX_NUM( segment->edge, edges ); 396 } 397 398 399 static int 400 af_get_strong_edge_index( AF_GlyphHints hints, 401 AF_Edge* strong_edges, 402 int dimension ) 403 { 404 AF_AxisHints axis = &hints->axis[dimension]; 405 AF_Edge edges = axis->edges; 406 407 408 return AF_INDEX_NUM( strong_edges[dimension], edges ); 409 } 410 411 412 #ifdef __cplusplus 413 extern "C" { 414 #endif 415 void 416 af_glyph_hints_dump_points( AF_GlyphHints hints, 417 FT_Bool to_stdout ) 418 { 419 AF_Point points = hints->points; 420 AF_Point limit = points + hints->num_points; 421 AF_Point* contour = hints->contours; 422 AF_Point* climit = contour + hints->num_contours; 423 AF_Point point; 424 425 426 AF_DUMP(( "Table of points:\n" )); 427 428 if ( hints->num_points ) 429 { 430 AF_DUMP(( " index hedge hseg vedge vseg flags " 431 /* " XXXXX XXXXX XXXXX XXXXX XXXXX XXXXXX" */ 432 " xorg yorg xscale yscale xfit yfit " 433 /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */ 434 " hbef haft vbef vaft" )); 435 /* " XXXXX XXXXX XXXXX XXXXX" */ 436 } 437 else 438 AF_DUMP(( " (none)\n" )); 439 440 for ( point = points; point < limit; point++ ) 441 { 442 int point_idx = AF_INDEX_NUM( point, points ); 443 int segment_idx_0 = af_get_segment_index( hints, point_idx, 0 ); 444 int segment_idx_1 = af_get_segment_index( hints, point_idx, 1 ); 445 446 char buf1[16], buf2[16], buf3[16], buf4[16]; 447 char buf5[16], buf6[16], buf7[16], buf8[16]; 448 449 450 /* insert extra newline at the beginning of a contour */ 451 if ( contour < climit && *contour == point ) 452 { 453 AF_DUMP(( "\n" )); 454 contour++; 455 } 456 457 AF_DUMP(( " %5d %5s %5s %5s %5s %s" 458 " %5d %5d %7.2f %7.2f %7.2f %7.2f" 459 " %5s %5s %5s %5s\n", 460 point_idx, 461 af_print_idx( buf1, 16, 462 af_get_edge_index( hints, segment_idx_1, 1 ) ), 463 af_print_idx( buf2, 16, segment_idx_1 ), 464 af_print_idx( buf3, 16, 465 af_get_edge_index( hints, segment_idx_0, 0 ) ), 466 af_print_idx( buf4, 16, segment_idx_0 ), 467 ( point->flags & AF_FLAG_NEAR ) 468 ? " near " 469 : ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) 470 ? " weak " 471 : "strong", 472 473 point->fx, 474 point->fy, 475 (double)point->ox / 64, 476 (double)point->oy / 64, 477 (double)point->x / 64, 478 (double)point->y / 64, 479 480 af_print_idx( buf5, 16, 481 af_get_strong_edge_index( hints, 482 point->before, 483 1 ) ), 484 af_print_idx( buf6, 16, 485 af_get_strong_edge_index( hints, 486 point->after, 487 1 ) ), 488 af_print_idx( buf7, 16, 489 af_get_strong_edge_index( hints, 490 point->before, 491 0 ) ), 492 af_print_idx( buf8, 16, 493 af_get_strong_edge_index( hints, 494 point->after, 495 0 ) ) )); 496 } 497 AF_DUMP(( "\n" )); 498 } 499 #ifdef __cplusplus 500 } 501 #endif 502 503 504 static const char* 505 af_edge_flags_to_string( FT_UInt flags ) 506 { 507 static char temp[32]; 508 int pos = 0; 509 510 511 if ( flags & AF_EDGE_ROUND ) 512 { 513 ft_memcpy( temp + pos, "round", 5 ); 514 pos += 5; 515 } 516 if ( flags & AF_EDGE_SERIF ) 517 { 518 if ( pos > 0 ) 519 temp[pos++] = ' '; 520 ft_memcpy( temp + pos, "serif", 5 ); 521 pos += 5; 522 } 523 if ( pos == 0 ) 524 return "normal"; 525 526 temp[pos] = '\0'; 527 528 return temp; 529 } 530 531 532 /* Dump the array of linked segments. */ 533 534 #ifdef __cplusplus 535 extern "C" { 536 #endif 537 void 538 af_glyph_hints_dump_segments( AF_GlyphHints hints, 539 FT_Bool to_stdout ) 540 { 541 FT_Int dimension; 542 543 544 for ( dimension = 1; dimension >= 0; dimension-- ) 545 { 546 AF_AxisHints axis = &hints->axis[dimension]; 547 AF_Point points = hints->points; 548 AF_Edge edges = axis->edges; 549 AF_Segment segments = axis->segments; 550 AF_Segment limit = segments + axis->num_segments; 551 AF_Segment seg; 552 553 char buf1[16], buf2[16], buf3[16]; 554 555 556 AF_DUMP(( "Table of %s segments:\n", 557 dimension == AF_DIMENSION_HORZ ? "vertical" 558 : "horizontal" )); 559 if ( axis->num_segments ) 560 { 561 AF_DUMP(( " index pos delta dir from to " 562 /* " XXXXX XXXXX XXXXX XXXXX XXXX XXXX" */ 563 " link serif edge" 564 /* " XXXX XXXXX XXXX" */ 565 " height extra flags\n" )); 566 /* " XXXXXX XXXXX XXXXXXXXXXX" */ 567 } 568 else 569 AF_DUMP(( " (none)\n" )); 570 571 for ( seg = segments; seg < limit; seg++ ) 572 AF_DUMP(( " %5d %5d %5d %5s %4d %4d" 573 " %4s %5s %4s" 574 " %6d %5d %11s\n", 575 AF_INDEX_NUM( seg, segments ), 576 seg->pos, 577 seg->delta, 578 af_dir_str( (AF_Direction)seg->dir ), 579 AF_INDEX_NUM( seg->first, points ), 580 AF_INDEX_NUM( seg->last, points ), 581 582 af_print_idx( buf1, 16, 583 AF_INDEX_NUM( seg->link, segments ) ), 584 af_print_idx( buf2, 16, 585 AF_INDEX_NUM( seg->serif, segments ) ), 586 af_print_idx( buf3, 16, 587 AF_INDEX_NUM( seg->edge, edges ) ), 588 589 seg->height, 590 seg->height - ( seg->max_coord - seg->min_coord ), 591 af_edge_flags_to_string( seg->flags ) )); 592 AF_DUMP(( "\n" )); 593 } 594 } 595 #ifdef __cplusplus 596 } 597 #endif 598 599 600 /* Fetch number of segments. */ 601 602 #ifdef __cplusplus 603 extern "C" { 604 #endif 605 FT_Error 606 af_glyph_hints_get_num_segments( AF_GlyphHints hints, 607 FT_Int dimension, 608 FT_UInt* num_segments ) 609 { 610 AF_Dimension dim; 611 AF_AxisHints axis; 612 613 614 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT; 615 616 axis = &hints->axis[dim]; 617 *num_segments = axis->num_segments; 618 619 return FT_Err_Ok; 620 } 621 #ifdef __cplusplus 622 } 623 #endif 624 625 626 /* Fetch offset of segments into user supplied offset array. */ 627 628 #ifdef __cplusplus 629 extern "C" { 630 #endif 631 FT_Error 632 af_glyph_hints_get_segment_offset( AF_GlyphHints hints, 633 FT_Int dimension, 634 FT_UInt idx, 635 FT_Pos *offset, 636 FT_Bool *is_blue, 637 FT_Pos *blue_offset ) 638 { 639 AF_Dimension dim; 640 AF_AxisHints axis; 641 AF_Segment seg; 642 643 644 if ( !offset ) 645 return FT_THROW( Invalid_Argument ); 646 647 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT; 648 649 axis = &hints->axis[dim]; 650 651 if ( idx >= axis->num_segments ) 652 return FT_THROW( Invalid_Argument ); 653 654 seg = &axis->segments[idx]; 655 *offset = ( dim == AF_DIMENSION_HORZ ) ? seg->first->fx 656 : seg->first->fy; 657 if ( seg->edge ) 658 *is_blue = FT_BOOL( seg->edge->blue_edge ); 659 else 660 *is_blue = FALSE; 661 662 if ( *is_blue ) 663 *blue_offset = seg->edge->blue_edge->org; 664 else 665 *blue_offset = 0; 666 667 return FT_Err_Ok; 668 } 669 #ifdef __cplusplus 670 } 671 #endif 672 673 674 /* Dump the array of linked edges. */ 675 676 #ifdef __cplusplus 677 extern "C" { 678 #endif 679 void 680 af_glyph_hints_dump_edges( AF_GlyphHints hints, 681 FT_Bool to_stdout ) 682 { 683 FT_Int dimension; 684 685 686 for ( dimension = 1; dimension >= 0; dimension-- ) 687 { 688 AF_AxisHints axis = &hints->axis[dimension]; 689 AF_Edge edges = axis->edges; 690 AF_Edge limit = edges + axis->num_edges; 691 AF_Edge edge; 692 693 char buf1[16], buf2[16]; 694 695 696 /* 697 * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges 698 * since they have a constant X coordinate. 699 */ 700 if ( dimension == AF_DIMENSION_HORZ ) 701 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n", 702 "vertical", 703 65536 * 64 / (double)hints->x_scale, 704 10 * (double)hints->x_scale / 65536 / 64 )); 705 else 706 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n", 707 "horizontal", 708 65536 * 64 / (double)hints->y_scale, 709 10 * (double)hints->y_scale / 65536 / 64 )); 710 711 if ( axis->num_edges ) 712 { 713 AF_DUMP(( " index pos dir link serif" 714 /* " XXXXX XXXX.XX XXXXX XXXX XXXXX" */ 715 " blue opos pos flags\n" )); 716 /* " X XXXX.XX XXXX.XX XXXXXXXXXXX" */ 717 } 718 else 719 AF_DUMP(( " (none)\n" )); 720 721 for ( edge = edges; edge < limit; edge++ ) 722 AF_DUMP(( " %5d %7.2f %5s %4s %5s" 723 " %c %7.2f %7.2f %11s\n", 724 AF_INDEX_NUM( edge, edges ), 725 (double)(int)edge->opos / 64, 726 af_dir_str( (AF_Direction)edge->dir ), 727 af_print_idx( buf1, 16, 728 AF_INDEX_NUM( edge->link, edges ) ), 729 af_print_idx( buf2, 16, 730 AF_INDEX_NUM( edge->serif, edges ) ), 731 732 edge->blue_edge ? 'y' : 'n', 733 (double)edge->opos / 64, 734 (double)edge->pos / 64, 735 af_edge_flags_to_string( edge->flags ) )); 736 AF_DUMP(( "\n" )); 737 } 738 } 739 #ifdef __cplusplus 740 } 741 #endif 742 743 #undef AF_DUMP 744 745 #endif /* !FT_DEBUG_AUTOFIT */ 746 747 748 /* Compute the direction value of a given vector. */ 749 750 FT_LOCAL_DEF( AF_Direction ) 751 af_direction_compute( FT_Pos dx, 752 FT_Pos dy ) 753 { 754 FT_Pos ll, ss; /* long and short arm lengths */ 755 AF_Direction dir; /* candidate direction */ 756 757 758 if ( dy >= dx ) 759 { 760 if ( dy >= -dx ) 761 { 762 dir = AF_DIR_UP; 763 ll = dy; 764 ss = dx; 765 } 766 else 767 { 768 dir = AF_DIR_LEFT; 769 ll = -dx; 770 ss = dy; 771 } 772 } 773 else /* dy < dx */ 774 { 775 if ( dy >= -dx ) 776 { 777 dir = AF_DIR_RIGHT; 778 ll = dx; 779 ss = dy; 780 } 781 else 782 { 783 dir = AF_DIR_DOWN; 784 ll = -dy; 785 ss = dx; 786 } 787 } 788 789 /* return no direction if arm lengths do not differ enough */ 790 /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */ 791 /* the long arm is never negative */ 792 if ( ll <= 14 * FT_ABS( ss ) ) 793 dir = AF_DIR_NONE; 794 795 return dir; 796 } 797 798 799 FT_LOCAL_DEF( void ) 800 af_glyph_hints_init( AF_GlyphHints hints, 801 FT_Memory memory ) 802 { 803 /* no need to initialize the embedded items */ 804 FT_MEM_ZERO( hints, sizeof ( *hints ) - sizeof ( hints->embedded ) ); 805 hints->memory = memory; 806 } 807 808 809 FT_LOCAL_DEF( void ) 810 af_glyph_hints_done( AF_GlyphHints hints ) 811 { 812 FT_Memory memory; 813 int dim; 814 815 816 if ( !( hints && hints->memory ) ) 817 return; 818 819 memory = hints->memory; 820 821 /* 822 * note that we don't need to free the segment and edge 823 * buffers since they are really within the hints->points array 824 */ 825 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) 826 { 827 AF_AxisHints axis = &hints->axis[dim]; 828 829 830 axis->num_segments = 0; 831 axis->max_segments = 0; 832 if ( axis->segments != axis->embedded.segments ) 833 FT_FREE( axis->segments ); 834 835 axis->num_edges = 0; 836 axis->max_edges = 0; 837 if ( axis->edges != axis->embedded.edges ) 838 FT_FREE( axis->edges ); 839 } 840 841 if ( hints->contours != hints->embedded.contours ) 842 FT_FREE( hints->contours ); 843 if ( hints->contour_y_minima != hints->embedded.contour_y_minima ) 844 FT_FREE( hints->contour_y_minima ); 845 if ( hints->contour_y_maxima != hints->embedded.contour_y_maxima ) 846 FT_FREE( hints->contour_y_maxima ); 847 hints->max_contours = 0; 848 hints->num_contours = 0; 849 850 if ( hints->points != hints->embedded.points ) 851 FT_FREE( hints->points ); 852 hints->max_points = 0; 853 hints->num_points = 0; 854 855 hints->memory = NULL; 856 } 857 858 859 /* Reset metrics. */ 860 861 FT_LOCAL_DEF( void ) 862 af_glyph_hints_rescale( AF_GlyphHints hints, 863 AF_StyleMetrics metrics ) 864 { 865 hints->metrics = metrics; 866 hints->scaler_flags = metrics->scaler.flags; 867 } 868 869 870 /* Recompute all AF_Point in AF_GlyphHints from the definitions */ 871 /* in a source outline. */ 872 873 FT_LOCAL_DEF( FT_Error ) 874 af_glyph_hints_reload( AF_GlyphHints hints, 875 FT_Outline* outline ) 876 { 877 FT_Error error = FT_Err_Ok; 878 AF_Point points; 879 FT_Int old_max, new_max; 880 FT_Fixed x_scale = hints->x_scale; 881 FT_Fixed y_scale = hints->y_scale; 882 FT_Pos x_delta = hints->x_delta; 883 FT_Pos y_delta = hints->y_delta; 884 FT_Memory memory = hints->memory; 885 886 887 hints->num_points = 0; 888 hints->num_contours = 0; 889 890 hints->axis[0].num_segments = 0; 891 hints->axis[0].num_edges = 0; 892 hints->axis[1].num_segments = 0; 893 hints->axis[1].num_edges = 0; 894 895 /* first of all, reallocate the contours array if necessary */ 896 new_max = outline->n_contours; 897 old_max = hints->max_contours; 898 899 if ( new_max <= AF_CONTOURS_EMBEDDED ) 900 { 901 if ( !hints->contours ) 902 { 903 hints->contours = hints->embedded.contours; 904 hints->contour_y_minima = hints->embedded.contour_y_minima; 905 hints->contour_y_maxima = hints->embedded.contour_y_maxima; 906 907 hints->max_contours = AF_CONTOURS_EMBEDDED; 908 } 909 } 910 else if ( new_max > old_max ) 911 { 912 if ( hints->contours == hints->embedded.contours ) 913 { 914 hints->contours = NULL; 915 hints->contour_y_minima = NULL; 916 hints->contour_y_maxima = NULL; 917 } 918 919 new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */ 920 921 if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) 922 goto Exit; 923 if ( FT_RENEW_ARRAY( hints->contour_y_minima, old_max, new_max ) ) 924 goto Exit; 925 if ( FT_RENEW_ARRAY( hints->contour_y_maxima, old_max, new_max ) ) 926 goto Exit; 927 928 hints->max_contours = new_max; 929 } 930 931 /* 932 * then reallocate the points arrays if necessary -- 933 * note that we reserve two additional point positions, used to 934 * hint metrics appropriately 935 */ 936 new_max = outline->n_points + 2; 937 old_max = hints->max_points; 938 939 if ( new_max <= AF_POINTS_EMBEDDED ) 940 { 941 if ( !hints->points ) 942 { 943 hints->points = hints->embedded.points; 944 hints->max_points = AF_POINTS_EMBEDDED; 945 } 946 } 947 else if ( new_max > old_max ) 948 { 949 if ( hints->points == hints->embedded.points ) 950 hints->points = NULL; 951 952 new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */ 953 954 if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) ) 955 goto Exit; 956 957 hints->max_points = new_max; 958 } 959 960 hints->num_points = outline->n_points; 961 hints->num_contours = outline->n_contours; 962 963 /* We can't rely on the value of `FT_Outline.flags' to know the fill */ 964 /* direction used for a glyph, given that some fonts are broken (e.g., */ 965 /* the Arphic ones). We thus recompute it each time we need to. */ 966 /* */ 967 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP; 968 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT; 969 970 if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT ) 971 { 972 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN; 973 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT; 974 } 975 976 hints->x_scale = x_scale; 977 hints->y_scale = y_scale; 978 hints->x_delta = x_delta; 979 hints->y_delta = y_delta; 980 981 points = hints->points; 982 if ( hints->num_points == 0 ) 983 goto Exit; 984 985 { 986 AF_Point point; 987 AF_Point point_limit = points + hints->num_points; 988 989 /* value 20 in `near_limit' is heuristic */ 990 FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM; 991 FT_Int near_limit = 20 * units_per_em / 2048; 992 993 994 /* compute coordinates & Bezier flags, next and prev */ 995 { 996 FT_Vector* vec = outline->points; 997 FT_Byte* tag = outline->tags; 998 FT_UShort endpoint = outline->contours[0]; 999 AF_Point end = points + endpoint; 1000 AF_Point prev = end; 1001 FT_Int contour_index = 0; 1002 1003 1004 for ( point = points; point < point_limit; point++, vec++, tag++ ) 1005 { 1006 FT_Pos out_x, out_y; 1007 1008 1009 point->in_dir = (FT_Char)AF_DIR_NONE; 1010 point->out_dir = (FT_Char)AF_DIR_NONE; 1011 1012 point->fx = (FT_Short)vec->x; 1013 point->fy = (FT_Short)vec->y; 1014 point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta; 1015 point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta; 1016 1017 end->fx = (FT_Short)outline->points[endpoint].x; 1018 end->fy = (FT_Short)outline->points[endpoint].y; 1019 1020 switch ( FT_CURVE_TAG( *tag ) ) 1021 { 1022 case FT_CURVE_TAG_CONIC: 1023 point->flags = AF_FLAG_CONIC; 1024 break; 1025 case FT_CURVE_TAG_CUBIC: 1026 point->flags = AF_FLAG_CUBIC; 1027 break; 1028 default: 1029 point->flags = AF_FLAG_NONE; 1030 } 1031 1032 out_x = point->fx - prev->fx; 1033 out_y = point->fy - prev->fy; 1034 1035 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit ) 1036 prev->flags |= AF_FLAG_NEAR; 1037 1038 point->prev = prev; 1039 prev->next = point; 1040 prev = point; 1041 1042 if ( point == end ) 1043 { 1044 if ( ++contour_index < outline->n_contours ) 1045 { 1046 endpoint = outline->contours[contour_index]; 1047 end = points + endpoint; 1048 prev = end; 1049 } 1050 } 1051 1052 #ifdef FT_DEBUG_AUTOFIT 1053 point->before[0] = NULL; 1054 point->before[1] = NULL; 1055 point->after[0] = NULL; 1056 point->after[1] = NULL; 1057 #endif 1058 1059 } 1060 } 1061 1062 /* set up the contours array */ 1063 { 1064 AF_Point* contour = hints->contours; 1065 AF_Point* contour_limit = contour + hints->num_contours; 1066 FT_UShort* end = outline->contours; 1067 FT_Int idx = 0; 1068 1069 1070 for ( ; contour < contour_limit; contour++, end++ ) 1071 { 1072 contour[0] = points + idx; 1073 idx = *end + 1; 1074 } 1075 } 1076 1077 { 1078 /* 1079 * Compute directions of `in' and `out' vectors. 1080 * 1081 * Note that distances between points that are very near to each 1082 * other are accumulated. In other words, the auto-hinter either 1083 * prepends the small vectors between near points to the first 1084 * non-near vector, or the sum of small vector lengths exceeds a 1085 * threshold, thus `grouping' the small vectors. All intermediate 1086 * points are tagged as weak; the directions are adjusted also to 1087 * be equal to the accumulated one. 1088 */ 1089 1090 FT_Int near_limit2 = 2 * near_limit - 1; 1091 1092 AF_Point* contour; 1093 AF_Point* contour_limit = hints->contours + hints->num_contours; 1094 1095 1096 for ( contour = hints->contours; contour < contour_limit; contour++ ) 1097 { 1098 AF_Point first = *contour; 1099 AF_Point next, prev, curr; 1100 1101 FT_Pos out_x, out_y; 1102 1103 1104 /* since the first point of a contour could be part of a */ 1105 /* series of near points, go backwards to find the first */ 1106 /* non-near point and adjust `first' */ 1107 1108 point = first; 1109 prev = first->prev; 1110 1111 while ( prev != first ) 1112 { 1113 out_x = point->fx - prev->fx; 1114 out_y = point->fy - prev->fy; 1115 1116 /* 1117 * We use Taxicab metrics to measure the vector length. 1118 * 1119 * Note that the accumulated distances so far could have the 1120 * opposite direction of the distance measured here. For this 1121 * reason we use `near_limit2' for the comparison to get a 1122 * non-near point even in the worst case. 1123 */ 1124 if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 ) 1125 break; 1126 1127 point = prev; 1128 prev = prev->prev; 1129 } 1130 1131 /* adjust first point */ 1132 first = point; 1133 1134 /* now loop over all points of the contour to get */ 1135 /* `in' and `out' vector directions */ 1136 1137 curr = first; 1138 1139 /* 1140 * We abuse the `u' and `v' fields to store index deltas to the 1141 * next and previous non-near point, respectively. 1142 * 1143 * To avoid problems with not having non-near points, we point to 1144 * `first' by default as the next non-near point. 1145 * 1146 */ 1147 curr->u = (FT_Pos)( first - curr ); 1148 first->v = -curr->u; 1149 1150 out_x = 0; 1151 out_y = 0; 1152 1153 next = first; 1154 do 1155 { 1156 AF_Direction out_dir; 1157 1158 1159 point = next; 1160 next = point->next; 1161 1162 out_x += next->fx - point->fx; 1163 out_y += next->fy - point->fy; 1164 1165 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit ) 1166 { 1167 next->flags |= AF_FLAG_WEAK_INTERPOLATION; 1168 continue; 1169 } 1170 1171 curr->u = (FT_Pos)( next - curr ); 1172 next->v = -curr->u; 1173 1174 out_dir = af_direction_compute( out_x, out_y ); 1175 1176 /* adjust directions for all points inbetween; */ 1177 /* the loop also updates position of `curr' */ 1178 curr->out_dir = (FT_Char)out_dir; 1179 for ( curr = curr->next; curr != next; curr = curr->next ) 1180 { 1181 curr->in_dir = (FT_Char)out_dir; 1182 curr->out_dir = (FT_Char)out_dir; 1183 } 1184 next->in_dir = (FT_Char)out_dir; 1185 1186 curr->u = (FT_Pos)( first - curr ); 1187 first->v = -curr->u; 1188 1189 out_x = 0; 1190 out_y = 0; 1191 1192 } while ( next != first ); 1193 } 1194 1195 /* 1196 * The next step is to `simplify' an outline's topology so that we 1197 * can identify local extrema more reliably: A series of 1198 * non-horizontal or non-vertical vectors pointing into the same 1199 * quadrant are handled as a single, long vector. From a 1200 * topological point of the view, the intermediate points are of no 1201 * interest and thus tagged as weak. 1202 */ 1203 1204 for ( point = points; point < point_limit; point++ ) 1205 { 1206 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) 1207 continue; 1208 1209 if ( point->in_dir == AF_DIR_NONE && 1210 point->out_dir == AF_DIR_NONE ) 1211 { 1212 /* check whether both vectors point into the same quadrant */ 1213 1214 FT_Pos in_x, in_y; 1215 FT_Pos out_x, out_y; 1216 1217 AF_Point next_u = point + point->u; 1218 AF_Point prev_v = point + point->v; 1219 1220 1221 in_x = point->fx - prev_v->fx; 1222 in_y = point->fy - prev_v->fy; 1223 1224 out_x = next_u->fx - point->fx; 1225 out_y = next_u->fy - point->fy; 1226 1227 if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 ) 1228 { 1229 /* yes, so tag current point as weak */ 1230 /* and update index deltas */ 1231 1232 point->flags |= AF_FLAG_WEAK_INTERPOLATION; 1233 1234 prev_v->u = (FT_Pos)( next_u - prev_v ); 1235 next_u->v = -prev_v->u; 1236 } 1237 } 1238 } 1239 1240 /* 1241 * Finally, check for remaining weak points. Everything else not 1242 * collected in edges so far is then implicitly classified as strong 1243 * points. 1244 */ 1245 1246 for ( point = points; point < point_limit; point++ ) 1247 { 1248 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) 1249 continue; 1250 1251 if ( point->flags & AF_FLAG_CONTROL ) 1252 { 1253 /* control points are always weak */ 1254 Is_Weak_Point: 1255 point->flags |= AF_FLAG_WEAK_INTERPOLATION; 1256 } 1257 else if ( point->out_dir == point->in_dir ) 1258 { 1259 if ( point->out_dir != AF_DIR_NONE ) 1260 { 1261 /* current point lies on a horizontal or */ 1262 /* vertical segment (but doesn't start or end it) */ 1263 goto Is_Weak_Point; 1264 } 1265 1266 { 1267 AF_Point next_u = point + point->u; 1268 AF_Point prev_v = point + point->v; 1269 1270 1271 if ( ft_corner_is_flat( point->fx - prev_v->fx, 1272 point->fy - prev_v->fy, 1273 next_u->fx - point->fx, 1274 next_u->fy - point->fy ) ) 1275 { 1276 /* either the `in' or the `out' vector is much more */ 1277 /* dominant than the other one, so tag current point */ 1278 /* as weak and update index deltas */ 1279 1280 prev_v->u = (FT_Pos)( next_u - prev_v ); 1281 next_u->v = -prev_v->u; 1282 1283 goto Is_Weak_Point; 1284 } 1285 } 1286 } 1287 else if ( point->in_dir == -point->out_dir ) 1288 { 1289 /* current point forms a spike */ 1290 goto Is_Weak_Point; 1291 } 1292 } 1293 } 1294 } 1295 1296 Exit: 1297 return error; 1298 } 1299 1300 1301 /* Store the hinted outline in an FT_Outline structure. */ 1302 1303 FT_LOCAL_DEF( void ) 1304 af_glyph_hints_save( AF_GlyphHints hints, 1305 FT_Outline* outline ) 1306 { 1307 AF_Point point = hints->points; 1308 AF_Point limit = point + hints->num_points; 1309 FT_Vector* vec = outline->points; 1310 FT_Byte* tag = outline->tags; 1311 1312 1313 for ( ; point < limit; point++, vec++, tag++ ) 1314 { 1315 vec->x = point->x; 1316 vec->y = point->y; 1317 1318 if ( point->flags & AF_FLAG_CONIC ) 1319 tag[0] = FT_CURVE_TAG_CONIC; 1320 else if ( point->flags & AF_FLAG_CUBIC ) 1321 tag[0] = FT_CURVE_TAG_CUBIC; 1322 else 1323 tag[0] = FT_CURVE_TAG_ON; 1324 } 1325 } 1326 1327 1328 /**************************************************************** 1329 * 1330 * EDGE POINT GRID-FITTING 1331 * 1332 ****************************************************************/ 1333 1334 1335 /* Align all points of an edge to the same coordinate value, */ 1336 /* either horizontally or vertically. */ 1337 1338 FT_LOCAL_DEF( void ) 1339 af_glyph_hints_align_edge_points( AF_GlyphHints hints, 1340 AF_Dimension dim ) 1341 { 1342 AF_AxisHints axis = &hints->axis[dim]; 1343 AF_Segment segments = axis->segments; 1344 AF_Segment segment_limit = FT_OFFSET( segments, axis->num_segments ); 1345 AF_Segment seg; 1346 1347 1348 if ( dim == AF_DIMENSION_HORZ ) 1349 { 1350 for ( seg = segments; seg < segment_limit; seg++ ) 1351 { 1352 AF_Edge edge = seg->edge; 1353 AF_Point point, first, last; 1354 1355 1356 if ( !edge ) 1357 continue; 1358 1359 first = seg->first; 1360 last = seg->last; 1361 point = first; 1362 for (;;) 1363 { 1364 point->x = edge->pos; 1365 point->flags |= AF_FLAG_TOUCH_X; 1366 1367 if ( point == last ) 1368 break; 1369 1370 point = point->next; 1371 } 1372 } 1373 } 1374 else 1375 { 1376 for ( seg = segments; seg < segment_limit; seg++ ) 1377 { 1378 AF_Edge edge = seg->edge; 1379 AF_Point point, first, last; 1380 1381 1382 if ( !edge ) 1383 continue; 1384 1385 first = seg->first; 1386 last = seg->last; 1387 point = first; 1388 for (;;) 1389 { 1390 point->y = edge->pos; 1391 point->flags |= AF_FLAG_TOUCH_Y; 1392 1393 if ( point == last ) 1394 break; 1395 1396 point = point->next; 1397 } 1398 } 1399 } 1400 } 1401 1402 1403 /**************************************************************** 1404 * 1405 * STRONG POINT INTERPOLATION 1406 * 1407 ****************************************************************/ 1408 1409 1410 /* Hint the strong points -- this is equivalent to the TrueType `IP' */ 1411 /* hinting instruction. */ 1412 1413 FT_LOCAL_DEF( void ) 1414 af_glyph_hints_align_strong_points( AF_GlyphHints hints, 1415 AF_Dimension dim ) 1416 { 1417 AF_Point points = hints->points; 1418 AF_Point point_limit = points + hints->num_points; 1419 AF_AxisHints axis = &hints->axis[dim]; 1420 AF_Edge edges = axis->edges; 1421 AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges ); 1422 FT_UInt touch_flag; 1423 1424 1425 if ( dim == AF_DIMENSION_HORZ ) 1426 touch_flag = AF_FLAG_TOUCH_X; 1427 else 1428 touch_flag = AF_FLAG_TOUCH_Y; 1429 1430 if ( edges < edge_limit ) 1431 { 1432 AF_Point point; 1433 AF_Edge edge; 1434 1435 1436 for ( point = points; point < point_limit; point++ ) 1437 { 1438 FT_Pos u, ou, fu; /* point position */ 1439 FT_Pos delta; 1440 1441 1442 if ( point->flags & touch_flag ) 1443 continue; 1444 1445 /* if this point is candidate to weak interpolation, we */ 1446 /* interpolate it after all strong points have been processed */ 1447 1448 if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ) 1449 continue; 1450 1451 if ( dim == AF_DIMENSION_VERT ) 1452 { 1453 u = point->fy; 1454 ou = point->oy; 1455 } 1456 else 1457 { 1458 u = point->fx; 1459 ou = point->ox; 1460 } 1461 1462 fu = u; 1463 1464 /* is the point before the first edge? */ 1465 edge = edges; 1466 delta = edge->fpos - u; 1467 if ( delta >= 0 ) 1468 { 1469 u = edge->pos - ( edge->opos - ou ); 1470 1471 #ifdef FT_DEBUG_AUTOFIT 1472 point->before[dim] = edge; 1473 point->after[dim] = NULL; 1474 #endif 1475 1476 goto Store_Point; 1477 } 1478 1479 /* is the point after the last edge? */ 1480 edge = edge_limit - 1; 1481 delta = u - edge->fpos; 1482 if ( delta >= 0 ) 1483 { 1484 u = edge->pos + ( ou - edge->opos ); 1485 1486 #ifdef FT_DEBUG_AUTOFIT 1487 point->before[dim] = NULL; 1488 point->after[dim] = edge; 1489 #endif 1490 1491 goto Store_Point; 1492 } 1493 1494 { 1495 FT_PtrDist min, max, mid; 1496 FT_Pos fpos; 1497 1498 1499 /* find enclosing edges */ 1500 min = 0; 1501 max = edge_limit - edges; 1502 1503 #if 1 1504 /* for a small number of edges, a linear search is better */ 1505 if ( max <= 8 ) 1506 { 1507 FT_PtrDist nn; 1508 1509 1510 for ( nn = 0; nn < max; nn++ ) 1511 if ( edges[nn].fpos >= u ) 1512 break; 1513 1514 if ( edges[nn].fpos == u ) 1515 { 1516 u = edges[nn].pos; 1517 goto Store_Point; 1518 } 1519 min = nn; 1520 } 1521 else 1522 #endif 1523 while ( min < max ) 1524 { 1525 mid = ( max + min ) >> 1; 1526 edge = edges + mid; 1527 fpos = edge->fpos; 1528 1529 if ( u < fpos ) 1530 max = mid; 1531 else if ( u > fpos ) 1532 min = mid + 1; 1533 else 1534 { 1535 /* we are on the edge */ 1536 u = edge->pos; 1537 1538 #ifdef FT_DEBUG_AUTOFIT 1539 point->before[dim] = NULL; 1540 point->after[dim] = NULL; 1541 #endif 1542 1543 goto Store_Point; 1544 } 1545 } 1546 1547 /* point is not on an edge */ 1548 { 1549 AF_Edge before = edges + min - 1; 1550 AF_Edge after = edges + min + 0; 1551 1552 1553 #ifdef FT_DEBUG_AUTOFIT 1554 point->before[dim] = before; 1555 point->after[dim] = after; 1556 #endif 1557 1558 /* assert( before && after && before != after ) */ 1559 if ( before->scale == 0 ) 1560 before->scale = FT_DivFix( after->pos - before->pos, 1561 after->fpos - before->fpos ); 1562 1563 u = before->pos + FT_MulFix( fu - before->fpos, 1564 before->scale ); 1565 } 1566 } 1567 1568 Store_Point: 1569 /* save the point position */ 1570 if ( dim == AF_DIMENSION_HORZ ) 1571 point->x = u; 1572 else 1573 point->y = u; 1574 1575 point->flags |= touch_flag; 1576 } 1577 } 1578 } 1579 1580 1581 /**************************************************************** 1582 * 1583 * WEAK POINT INTERPOLATION 1584 * 1585 ****************************************************************/ 1586 1587 1588 /* Shift the original coordinates of all points between `p1' and */ 1589 /* `p2' to get hinted coordinates, using the same difference as */ 1590 /* given by `ref'. */ 1591 1592 static void 1593 af_iup_shift( AF_Point p1, 1594 AF_Point p2, 1595 AF_Point ref ) 1596 { 1597 AF_Point p; 1598 FT_Pos delta = ref->u - ref->v; 1599 1600 1601 if ( delta == 0 ) 1602 return; 1603 1604 for ( p = p1; p < ref; p++ ) 1605 p->u = p->v + delta; 1606 1607 for ( p = ref + 1; p <= p2; p++ ) 1608 p->u = p->v + delta; 1609 } 1610 1611 1612 /* Interpolate the original coordinates of all points between `p1' and */ 1613 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */ 1614 /* reference points. The `u' and `v' members are the current and */ 1615 /* original coordinate values, respectively. */ 1616 /* */ 1617 /* Details can be found in the TrueType bytecode specification. */ 1618 1619 static void 1620 af_iup_interp( AF_Point p1, 1621 AF_Point p2, 1622 AF_Point ref1, 1623 AF_Point ref2 ) 1624 { 1625 AF_Point p; 1626 FT_Pos u, v1, v2, u1, u2, d1, d2; 1627 1628 1629 if ( p1 > p2 ) 1630 return; 1631 1632 if ( ref1->v > ref2->v ) 1633 { 1634 p = ref1; 1635 ref1 = ref2; 1636 ref2 = p; 1637 } 1638 1639 v1 = ref1->v; 1640 v2 = ref2->v; 1641 u1 = ref1->u; 1642 u2 = ref2->u; 1643 d1 = u1 - v1; 1644 d2 = u2 - v2; 1645 1646 if ( u1 == u2 || v1 == v2 ) 1647 { 1648 for ( p = p1; p <= p2; p++ ) 1649 { 1650 u = p->v; 1651 1652 if ( u <= v1 ) 1653 u += d1; 1654 else if ( u >= v2 ) 1655 u += d2; 1656 else 1657 u = u1; 1658 1659 p->u = u; 1660 } 1661 } 1662 else 1663 { 1664 FT_Fixed scale = FT_DivFix( u2 - u1, v2 - v1 ); 1665 1666 1667 for ( p = p1; p <= p2; p++ ) 1668 { 1669 u = p->v; 1670 1671 if ( u <= v1 ) 1672 u += d1; 1673 else if ( u >= v2 ) 1674 u += d2; 1675 else 1676 u = u1 + FT_MulFix( u - v1, scale ); 1677 1678 p->u = u; 1679 } 1680 } 1681 } 1682 1683 1684 /* Hint the weak points -- this is equivalent to the TrueType `IUP' */ 1685 /* hinting instruction. */ 1686 1687 FT_LOCAL_DEF( void ) 1688 af_glyph_hints_align_weak_points( AF_GlyphHints hints, 1689 AF_Dimension dim ) 1690 { 1691 AF_Point points = hints->points; 1692 AF_Point point_limit = points + hints->num_points; 1693 AF_Point* contour = hints->contours; 1694 AF_Point* contour_limit = contour + hints->num_contours; 1695 FT_UInt touch_flag; 1696 AF_Point point; 1697 AF_Point end_point; 1698 AF_Point first_point; 1699 1700 1701 /* PASS 1: Move segment points to edge positions */ 1702 1703 if ( dim == AF_DIMENSION_HORZ ) 1704 { 1705 touch_flag = AF_FLAG_TOUCH_X; 1706 1707 for ( point = points; point < point_limit; point++ ) 1708 { 1709 point->u = point->x; 1710 point->v = point->ox; 1711 } 1712 } 1713 else 1714 { 1715 touch_flag = AF_FLAG_TOUCH_Y; 1716 1717 for ( point = points; point < point_limit; point++ ) 1718 { 1719 point->u = point->y; 1720 point->v = point->oy; 1721 } 1722 } 1723 1724 for ( ; contour < contour_limit; contour++ ) 1725 { 1726 AF_Point first_touched, last_touched; 1727 1728 1729 point = *contour; 1730 end_point = point->prev; 1731 first_point = point; 1732 1733 /* find first touched point */ 1734 for (;;) 1735 { 1736 if ( point > end_point ) /* no touched point in contour */ 1737 goto NextContour; 1738 1739 if ( point->flags & touch_flag ) 1740 break; 1741 1742 point++; 1743 } 1744 1745 first_touched = point; 1746 1747 for (;;) 1748 { 1749 FT_ASSERT( point <= end_point && 1750 ( point->flags & touch_flag ) != 0 ); 1751 1752 /* skip any touched neighbours */ 1753 while ( point < end_point && 1754 ( point[1].flags & touch_flag ) != 0 ) 1755 point++; 1756 1757 last_touched = point; 1758 1759 /* find the next touched point, if any */ 1760 point++; 1761 for (;;) 1762 { 1763 if ( point > end_point ) 1764 goto EndContour; 1765 1766 if ( ( point->flags & touch_flag ) != 0 ) 1767 break; 1768 1769 point++; 1770 } 1771 1772 /* interpolate between last_touched and point */ 1773 af_iup_interp( last_touched + 1, point - 1, 1774 last_touched, point ); 1775 } 1776 1777 EndContour: 1778 /* special case: only one point was touched */ 1779 if ( last_touched == first_touched ) 1780 af_iup_shift( first_point, end_point, first_touched ); 1781 1782 else /* interpolate the last part */ 1783 { 1784 if ( last_touched < end_point ) 1785 af_iup_interp( last_touched + 1, end_point, 1786 last_touched, first_touched ); 1787 1788 if ( first_touched > points ) 1789 af_iup_interp( first_point, first_touched - 1, 1790 last_touched, first_touched ); 1791 } 1792 1793 NextContour: 1794 ; 1795 } 1796 1797 /* now save the interpolated values back to x/y */ 1798 if ( dim == AF_DIMENSION_HORZ ) 1799 { 1800 for ( point = points; point < point_limit; point++ ) 1801 point->x = point->u; 1802 } 1803 else 1804 { 1805 for ( point = points; point < point_limit; point++ ) 1806 point->y = point->u; 1807 } 1808 } 1809 1810 1811 /* END */