1 /**************************************************************************** 2 * 3 * afhints.c 4 * 5 * Auto-fitter hinting routines (body). 6 * 7 * Copyright (C) 2003-2023 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 ) af_sort_pos(FT_UInt count,FT_Pos * table)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 ) af_sort_and_quantize_widths(FT_UInt * count,AF_Width table,FT_Pos threshold)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 ) af_axis_hints_new_segment(AF_AxisHints axis,FT_Memory memory,AF_Segment * asegment)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 ) af_axis_hints_new_edge(AF_AxisHints axis,FT_Int fpos,AF_Direction dir,FT_Bool top_to_bottom_hinting,FT_Memory memory,AF_Edge * anedge)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* af_dir_str(AF_Direction dir)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* af_print_idx(char * p,size_t n,int idx)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 af_get_segment_index(AF_GlyphHints hints,int point_idx,int dimension)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 af_get_edge_index(AF_GlyphHints hints,int segment_idx,int dimension)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 af_get_strong_edge_index(AF_GlyphHints hints,AF_Edge * strong_edges,int dimension)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 af_glyph_hints_dump_points(AF_GlyphHints hints,FT_Bool to_stdout)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* af_edge_flags_to_string(FT_UInt flags)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 af_glyph_hints_dump_segments(AF_GlyphHints hints,FT_Bool to_stdout)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 af_glyph_hints_get_num_segments(AF_GlyphHints hints,FT_Int dimension,FT_UInt * num_segments)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 af_glyph_hints_get_segment_offset(AF_GlyphHints hints,FT_Int dimension,FT_UInt idx,FT_Pos * offset,FT_Bool * is_blue,FT_Pos * blue_offset)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 af_glyph_hints_dump_edges(AF_GlyphHints hints,FT_Bool to_stdout)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 ) af_direction_compute(FT_Pos dx,FT_Pos dy)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 ) af_glyph_hints_init(AF_GlyphHints hints,FT_Memory memory)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 ) af_glyph_hints_done(AF_GlyphHints hints)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 hints->max_contours = 0; 844 hints->num_contours = 0; 845 846 if ( hints->points != hints->embedded.points ) 847 FT_FREE( hints->points ); 848 hints->max_points = 0; 849 hints->num_points = 0; 850 851 hints->memory = NULL; 852 } 853 854 855 /* Reset metrics. */ 856 857 FT_LOCAL_DEF( void ) af_glyph_hints_rescale(AF_GlyphHints hints,AF_StyleMetrics metrics)858 af_glyph_hints_rescale( AF_GlyphHints hints, 859 AF_StyleMetrics metrics ) 860 { 861 hints->metrics = metrics; 862 hints->scaler_flags = metrics->scaler.flags; 863 } 864 865 866 /* Recompute all AF_Point in AF_GlyphHints from the definitions */ 867 /* in a source outline. */ 868 869 FT_LOCAL_DEF( FT_Error ) af_glyph_hints_reload(AF_GlyphHints hints,FT_Outline * outline)870 af_glyph_hints_reload( AF_GlyphHints hints, 871 FT_Outline* outline ) 872 { 873 FT_Error error = FT_Err_Ok; 874 AF_Point points; 875 FT_Int old_max, new_max; 876 FT_Fixed x_scale = hints->x_scale; 877 FT_Fixed y_scale = hints->y_scale; 878 FT_Pos x_delta = hints->x_delta; 879 FT_Pos y_delta = hints->y_delta; 880 FT_Memory memory = hints->memory; 881 882 883 hints->num_points = 0; 884 hints->num_contours = 0; 885 886 hints->axis[0].num_segments = 0; 887 hints->axis[0].num_edges = 0; 888 hints->axis[1].num_segments = 0; 889 hints->axis[1].num_edges = 0; 890 891 /* first of all, reallocate the contours array if necessary */ 892 new_max = outline->n_contours; 893 old_max = hints->max_contours; 894 895 if ( new_max <= AF_CONTOURS_EMBEDDED ) 896 { 897 if ( !hints->contours ) 898 { 899 hints->contours = hints->embedded.contours; 900 hints->max_contours = AF_CONTOURS_EMBEDDED; 901 } 902 } 903 else if ( new_max > old_max ) 904 { 905 if ( hints->contours == hints->embedded.contours ) 906 hints->contours = NULL; 907 908 new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */ 909 910 if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) 911 goto Exit; 912 913 hints->max_contours = new_max; 914 } 915 916 /* 917 * then reallocate the points arrays if necessary -- 918 * note that we reserve two additional point positions, used to 919 * hint metrics appropriately 920 */ 921 new_max = outline->n_points + 2; 922 old_max = hints->max_points; 923 924 if ( new_max <= AF_POINTS_EMBEDDED ) 925 { 926 if ( !hints->points ) 927 { 928 hints->points = hints->embedded.points; 929 hints->max_points = AF_POINTS_EMBEDDED; 930 } 931 } 932 else if ( new_max > old_max ) 933 { 934 if ( hints->points == hints->embedded.points ) 935 hints->points = NULL; 936 937 new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */ 938 939 if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) ) 940 goto Exit; 941 942 hints->max_points = new_max; 943 } 944 945 hints->num_points = outline->n_points; 946 hints->num_contours = outline->n_contours; 947 948 /* We can't rely on the value of `FT_Outline.flags' to know the fill */ 949 /* direction used for a glyph, given that some fonts are broken (e.g., */ 950 /* the Arphic ones). We thus recompute it each time we need to. */ 951 /* */ 952 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP; 953 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT; 954 955 if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT ) 956 { 957 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN; 958 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT; 959 } 960 961 hints->x_scale = x_scale; 962 hints->y_scale = y_scale; 963 hints->x_delta = x_delta; 964 hints->y_delta = y_delta; 965 966 points = hints->points; 967 if ( hints->num_points == 0 ) 968 goto Exit; 969 970 { 971 AF_Point point; 972 AF_Point point_limit = points + hints->num_points; 973 974 /* value 20 in `near_limit' is heuristic */ 975 FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM; 976 FT_Int near_limit = 20 * units_per_em / 2048; 977 978 979 /* compute coordinates & Bezier flags, next and prev */ 980 { 981 FT_Vector* vec = outline->points; 982 char* tag = outline->tags; 983 FT_Short endpoint = outline->contours[0]; 984 AF_Point end = points + endpoint; 985 AF_Point prev = end; 986 FT_Int contour_index = 0; 987 988 989 for ( point = points; point < point_limit; point++, vec++, tag++ ) 990 { 991 FT_Pos out_x, out_y; 992 993 994 point->in_dir = (FT_Char)AF_DIR_NONE; 995 point->out_dir = (FT_Char)AF_DIR_NONE; 996 997 point->fx = (FT_Short)vec->x; 998 point->fy = (FT_Short)vec->y; 999 point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta; 1000 point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta; 1001 1002 end->fx = (FT_Short)outline->points[endpoint].x; 1003 end->fy = (FT_Short)outline->points[endpoint].y; 1004 1005 switch ( FT_CURVE_TAG( *tag ) ) 1006 { 1007 case FT_CURVE_TAG_CONIC: 1008 point->flags = AF_FLAG_CONIC; 1009 break; 1010 case FT_CURVE_TAG_CUBIC: 1011 point->flags = AF_FLAG_CUBIC; 1012 break; 1013 default: 1014 point->flags = AF_FLAG_NONE; 1015 } 1016 1017 out_x = point->fx - prev->fx; 1018 out_y = point->fy - prev->fy; 1019 1020 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit ) 1021 prev->flags |= AF_FLAG_NEAR; 1022 1023 point->prev = prev; 1024 prev->next = point; 1025 prev = point; 1026 1027 if ( point == end ) 1028 { 1029 if ( ++contour_index < outline->n_contours ) 1030 { 1031 endpoint = outline->contours[contour_index]; 1032 end = points + endpoint; 1033 prev = end; 1034 } 1035 } 1036 1037 #ifdef FT_DEBUG_AUTOFIT 1038 point->before[0] = NULL; 1039 point->before[1] = NULL; 1040 point->after[0] = NULL; 1041 point->after[1] = NULL; 1042 #endif 1043 1044 } 1045 } 1046 1047 /* set up the contours array */ 1048 { 1049 AF_Point* contour = hints->contours; 1050 AF_Point* contour_limit = contour + hints->num_contours; 1051 short* end = outline->contours; 1052 short idx = 0; 1053 1054 1055 for ( ; contour < contour_limit; contour++, end++ ) 1056 { 1057 contour[0] = points + idx; 1058 idx = (short)( end[0] + 1 ); 1059 } 1060 } 1061 1062 { 1063 /* 1064 * Compute directions of `in' and `out' vectors. 1065 * 1066 * Note that distances between points that are very near to each 1067 * other are accumulated. In other words, the auto-hinter either 1068 * prepends the small vectors between near points to the first 1069 * non-near vector, or the sum of small vector lengths exceeds a 1070 * threshold, thus `grouping' the small vectors. All intermediate 1071 * points are tagged as weak; the directions are adjusted also to 1072 * be equal to the accumulated one. 1073 */ 1074 1075 FT_Int near_limit2 = 2 * near_limit - 1; 1076 1077 AF_Point* contour; 1078 AF_Point* contour_limit = hints->contours + hints->num_contours; 1079 1080 1081 for ( contour = hints->contours; contour < contour_limit; contour++ ) 1082 { 1083 AF_Point first = *contour; 1084 AF_Point next, prev, curr; 1085 1086 FT_Pos out_x, out_y; 1087 1088 1089 /* since the first point of a contour could be part of a */ 1090 /* series of near points, go backwards to find the first */ 1091 /* non-near point and adjust `first' */ 1092 1093 point = first; 1094 prev = first->prev; 1095 1096 while ( prev != first ) 1097 { 1098 out_x = point->fx - prev->fx; 1099 out_y = point->fy - prev->fy; 1100 1101 /* 1102 * We use Taxicab metrics to measure the vector length. 1103 * 1104 * Note that the accumulated distances so far could have the 1105 * opposite direction of the distance measured here. For this 1106 * reason we use `near_limit2' for the comparison to get a 1107 * non-near point even in the worst case. 1108 */ 1109 if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 ) 1110 break; 1111 1112 point = prev; 1113 prev = prev->prev; 1114 } 1115 1116 /* adjust first point */ 1117 first = point; 1118 1119 /* now loop over all points of the contour to get */ 1120 /* `in' and `out' vector directions */ 1121 1122 curr = first; 1123 1124 /* 1125 * We abuse the `u' and `v' fields to store index deltas to the 1126 * next and previous non-near point, respectively. 1127 * 1128 * To avoid problems with not having non-near points, we point to 1129 * `first' by default as the next non-near point. 1130 * 1131 */ 1132 curr->u = (FT_Pos)( first - curr ); 1133 first->v = -curr->u; 1134 1135 out_x = 0; 1136 out_y = 0; 1137 1138 next = first; 1139 do 1140 { 1141 AF_Direction out_dir; 1142 1143 1144 point = next; 1145 next = point->next; 1146 1147 out_x += next->fx - point->fx; 1148 out_y += next->fy - point->fy; 1149 1150 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit ) 1151 { 1152 next->flags |= AF_FLAG_WEAK_INTERPOLATION; 1153 continue; 1154 } 1155 1156 curr->u = (FT_Pos)( next - curr ); 1157 next->v = -curr->u; 1158 1159 out_dir = af_direction_compute( out_x, out_y ); 1160 1161 /* adjust directions for all points inbetween; */ 1162 /* the loop also updates position of `curr' */ 1163 curr->out_dir = (FT_Char)out_dir; 1164 for ( curr = curr->next; curr != next; curr = curr->next ) 1165 { 1166 curr->in_dir = (FT_Char)out_dir; 1167 curr->out_dir = (FT_Char)out_dir; 1168 } 1169 next->in_dir = (FT_Char)out_dir; 1170 1171 curr->u = (FT_Pos)( first - curr ); 1172 first->v = -curr->u; 1173 1174 out_x = 0; 1175 out_y = 0; 1176 1177 } while ( next != first ); 1178 } 1179 1180 /* 1181 * The next step is to `simplify' an outline's topology so that we 1182 * can identify local extrema more reliably: A series of 1183 * non-horizontal or non-vertical vectors pointing into the same 1184 * quadrant are handled as a single, long vector. From a 1185 * topological point of the view, the intermediate points are of no 1186 * interest and thus tagged as weak. 1187 */ 1188 1189 for ( point = points; point < point_limit; point++ ) 1190 { 1191 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) 1192 continue; 1193 1194 if ( point->in_dir == AF_DIR_NONE && 1195 point->out_dir == AF_DIR_NONE ) 1196 { 1197 /* check whether both vectors point into the same quadrant */ 1198 1199 FT_Pos in_x, in_y; 1200 FT_Pos out_x, out_y; 1201 1202 AF_Point next_u = point + point->u; 1203 AF_Point prev_v = point + point->v; 1204 1205 1206 in_x = point->fx - prev_v->fx; 1207 in_y = point->fy - prev_v->fy; 1208 1209 out_x = next_u->fx - point->fx; 1210 out_y = next_u->fy - point->fy; 1211 1212 if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 ) 1213 { 1214 /* yes, so tag current point as weak */ 1215 /* and update index deltas */ 1216 1217 point->flags |= AF_FLAG_WEAK_INTERPOLATION; 1218 1219 prev_v->u = (FT_Pos)( next_u - prev_v ); 1220 next_u->v = -prev_v->u; 1221 } 1222 } 1223 } 1224 1225 /* 1226 * Finally, check for remaining weak points. Everything else not 1227 * collected in edges so far is then implicitly classified as strong 1228 * points. 1229 */ 1230 1231 for ( point = points; point < point_limit; point++ ) 1232 { 1233 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) 1234 continue; 1235 1236 if ( point->flags & AF_FLAG_CONTROL ) 1237 { 1238 /* control points are always weak */ 1239 Is_Weak_Point: 1240 point->flags |= AF_FLAG_WEAK_INTERPOLATION; 1241 } 1242 else if ( point->out_dir == point->in_dir ) 1243 { 1244 if ( point->out_dir != AF_DIR_NONE ) 1245 { 1246 /* current point lies on a horizontal or */ 1247 /* vertical segment (but doesn't start or end it) */ 1248 goto Is_Weak_Point; 1249 } 1250 1251 { 1252 AF_Point next_u = point + point->u; 1253 AF_Point prev_v = point + point->v; 1254 1255 1256 if ( ft_corner_is_flat( point->fx - prev_v->fx, 1257 point->fy - prev_v->fy, 1258 next_u->fx - point->fx, 1259 next_u->fy - point->fy ) ) 1260 { 1261 /* either the `in' or the `out' vector is much more */ 1262 /* dominant than the other one, so tag current point */ 1263 /* as weak and update index deltas */ 1264 1265 prev_v->u = (FT_Pos)( next_u - prev_v ); 1266 next_u->v = -prev_v->u; 1267 1268 goto Is_Weak_Point; 1269 } 1270 } 1271 } 1272 else if ( point->in_dir == -point->out_dir ) 1273 { 1274 /* current point forms a spike */ 1275 goto Is_Weak_Point; 1276 } 1277 } 1278 } 1279 } 1280 1281 Exit: 1282 return error; 1283 } 1284 1285 1286 /* Store the hinted outline in an FT_Outline structure. */ 1287 1288 FT_LOCAL_DEF( void ) af_glyph_hints_save(AF_GlyphHints hints,FT_Outline * outline)1289 af_glyph_hints_save( AF_GlyphHints hints, 1290 FT_Outline* outline ) 1291 { 1292 AF_Point point = hints->points; 1293 AF_Point limit = point + hints->num_points; 1294 FT_Vector* vec = outline->points; 1295 char* tag = outline->tags; 1296 1297 1298 for ( ; point < limit; point++, vec++, tag++ ) 1299 { 1300 vec->x = point->x; 1301 vec->y = point->y; 1302 1303 if ( point->flags & AF_FLAG_CONIC ) 1304 tag[0] = FT_CURVE_TAG_CONIC; 1305 else if ( point->flags & AF_FLAG_CUBIC ) 1306 tag[0] = FT_CURVE_TAG_CUBIC; 1307 else 1308 tag[0] = FT_CURVE_TAG_ON; 1309 } 1310 } 1311 1312 1313 /**************************************************************** 1314 * 1315 * EDGE POINT GRID-FITTING 1316 * 1317 ****************************************************************/ 1318 1319 1320 /* Align all points of an edge to the same coordinate value, */ 1321 /* either horizontally or vertically. */ 1322 1323 FT_LOCAL_DEF( void ) af_glyph_hints_align_edge_points(AF_GlyphHints hints,AF_Dimension dim)1324 af_glyph_hints_align_edge_points( AF_GlyphHints hints, 1325 AF_Dimension dim ) 1326 { 1327 AF_AxisHints axis = & hints->axis[dim]; 1328 AF_Segment segments = axis->segments; 1329 AF_Segment segment_limit = FT_OFFSET( segments, axis->num_segments ); 1330 AF_Segment seg; 1331 1332 1333 if ( dim == AF_DIMENSION_HORZ ) 1334 { 1335 for ( seg = segments; seg < segment_limit; seg++ ) 1336 { 1337 AF_Edge edge = seg->edge; 1338 AF_Point point, first, last; 1339 1340 1341 if ( !edge ) 1342 continue; 1343 1344 first = seg->first; 1345 last = seg->last; 1346 point = first; 1347 for (;;) 1348 { 1349 point->x = edge->pos; 1350 point->flags |= AF_FLAG_TOUCH_X; 1351 1352 if ( point == last ) 1353 break; 1354 1355 point = point->next; 1356 } 1357 } 1358 } 1359 else 1360 { 1361 for ( seg = segments; seg < segment_limit; seg++ ) 1362 { 1363 AF_Edge edge = seg->edge; 1364 AF_Point point, first, last; 1365 1366 1367 if ( !edge ) 1368 continue; 1369 1370 first = seg->first; 1371 last = seg->last; 1372 point = first; 1373 for (;;) 1374 { 1375 point->y = edge->pos; 1376 point->flags |= AF_FLAG_TOUCH_Y; 1377 1378 if ( point == last ) 1379 break; 1380 1381 point = point->next; 1382 } 1383 } 1384 } 1385 } 1386 1387 1388 /**************************************************************** 1389 * 1390 * STRONG POINT INTERPOLATION 1391 * 1392 ****************************************************************/ 1393 1394 1395 /* Hint the strong points -- this is equivalent to the TrueType `IP' */ 1396 /* hinting instruction. */ 1397 1398 FT_LOCAL_DEF( void ) af_glyph_hints_align_strong_points(AF_GlyphHints hints,AF_Dimension dim)1399 af_glyph_hints_align_strong_points( AF_GlyphHints hints, 1400 AF_Dimension dim ) 1401 { 1402 AF_Point points = hints->points; 1403 AF_Point point_limit = points + hints->num_points; 1404 AF_AxisHints axis = &hints->axis[dim]; 1405 AF_Edge edges = axis->edges; 1406 AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges ); 1407 FT_UInt touch_flag; 1408 1409 1410 if ( dim == AF_DIMENSION_HORZ ) 1411 touch_flag = AF_FLAG_TOUCH_X; 1412 else 1413 touch_flag = AF_FLAG_TOUCH_Y; 1414 1415 if ( edges < edge_limit ) 1416 { 1417 AF_Point point; 1418 AF_Edge edge; 1419 1420 1421 for ( point = points; point < point_limit; point++ ) 1422 { 1423 FT_Pos u, ou, fu; /* point position */ 1424 FT_Pos delta; 1425 1426 1427 if ( point->flags & touch_flag ) 1428 continue; 1429 1430 /* if this point is candidate to weak interpolation, we */ 1431 /* interpolate it after all strong points have been processed */ 1432 1433 if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ) 1434 continue; 1435 1436 if ( dim == AF_DIMENSION_VERT ) 1437 { 1438 u = point->fy; 1439 ou = point->oy; 1440 } 1441 else 1442 { 1443 u = point->fx; 1444 ou = point->ox; 1445 } 1446 1447 fu = u; 1448 1449 /* is the point before the first edge? */ 1450 edge = edges; 1451 delta = edge->fpos - u; 1452 if ( delta >= 0 ) 1453 { 1454 u = edge->pos - ( edge->opos - ou ); 1455 1456 #ifdef FT_DEBUG_AUTOFIT 1457 point->before[dim] = edge; 1458 point->after[dim] = NULL; 1459 #endif 1460 1461 goto Store_Point; 1462 } 1463 1464 /* is the point after the last edge? */ 1465 edge = edge_limit - 1; 1466 delta = u - edge->fpos; 1467 if ( delta >= 0 ) 1468 { 1469 u = edge->pos + ( ou - edge->opos ); 1470 1471 #ifdef FT_DEBUG_AUTOFIT 1472 point->before[dim] = NULL; 1473 point->after[dim] = edge; 1474 #endif 1475 1476 goto Store_Point; 1477 } 1478 1479 { 1480 FT_PtrDist min, max, mid; 1481 FT_Pos fpos; 1482 1483 1484 /* find enclosing edges */ 1485 min = 0; 1486 max = edge_limit - edges; 1487 1488 #if 1 1489 /* for a small number of edges, a linear search is better */ 1490 if ( max <= 8 ) 1491 { 1492 FT_PtrDist nn; 1493 1494 1495 for ( nn = 0; nn < max; nn++ ) 1496 if ( edges[nn].fpos >= u ) 1497 break; 1498 1499 if ( edges[nn].fpos == u ) 1500 { 1501 u = edges[nn].pos; 1502 goto Store_Point; 1503 } 1504 min = nn; 1505 } 1506 else 1507 #endif 1508 while ( min < max ) 1509 { 1510 mid = ( max + min ) >> 1; 1511 edge = edges + mid; 1512 fpos = edge->fpos; 1513 1514 if ( u < fpos ) 1515 max = mid; 1516 else if ( u > fpos ) 1517 min = mid + 1; 1518 else 1519 { 1520 /* we are on the edge */ 1521 u = edge->pos; 1522 1523 #ifdef FT_DEBUG_AUTOFIT 1524 point->before[dim] = NULL; 1525 point->after[dim] = NULL; 1526 #endif 1527 1528 goto Store_Point; 1529 } 1530 } 1531 1532 /* point is not on an edge */ 1533 { 1534 AF_Edge before = edges + min - 1; 1535 AF_Edge after = edges + min + 0; 1536 1537 1538 #ifdef FT_DEBUG_AUTOFIT 1539 point->before[dim] = before; 1540 point->after[dim] = after; 1541 #endif 1542 1543 /* assert( before && after && before != after ) */ 1544 if ( before->scale == 0 ) 1545 before->scale = FT_DivFix( after->pos - before->pos, 1546 after->fpos - before->fpos ); 1547 1548 u = before->pos + FT_MulFix( fu - before->fpos, 1549 before->scale ); 1550 } 1551 } 1552 1553 Store_Point: 1554 /* save the point position */ 1555 if ( dim == AF_DIMENSION_HORZ ) 1556 point->x = u; 1557 else 1558 point->y = u; 1559 1560 point->flags |= touch_flag; 1561 } 1562 } 1563 } 1564 1565 1566 /**************************************************************** 1567 * 1568 * WEAK POINT INTERPOLATION 1569 * 1570 ****************************************************************/ 1571 1572 1573 /* Shift the original coordinates of all points between `p1' and */ 1574 /* `p2' to get hinted coordinates, using the same difference as */ 1575 /* given by `ref'. */ 1576 1577 static void af_iup_shift(AF_Point p1,AF_Point p2,AF_Point ref)1578 af_iup_shift( AF_Point p1, 1579 AF_Point p2, 1580 AF_Point ref ) 1581 { 1582 AF_Point p; 1583 FT_Pos delta = ref->u - ref->v; 1584 1585 1586 if ( delta == 0 ) 1587 return; 1588 1589 for ( p = p1; p < ref; p++ ) 1590 p->u = p->v + delta; 1591 1592 for ( p = ref + 1; p <= p2; p++ ) 1593 p->u = p->v + delta; 1594 } 1595 1596 1597 /* Interpolate the original coordinates of all points between `p1' and */ 1598 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */ 1599 /* reference points. The `u' and `v' members are the current and */ 1600 /* original coordinate values, respectively. */ 1601 /* */ 1602 /* Details can be found in the TrueType bytecode specification. */ 1603 1604 static void af_iup_interp(AF_Point p1,AF_Point p2,AF_Point ref1,AF_Point ref2)1605 af_iup_interp( AF_Point p1, 1606 AF_Point p2, 1607 AF_Point ref1, 1608 AF_Point ref2 ) 1609 { 1610 AF_Point p; 1611 FT_Pos u, v1, v2, u1, u2, d1, d2; 1612 1613 1614 if ( p1 > p2 ) 1615 return; 1616 1617 if ( ref1->v > ref2->v ) 1618 { 1619 p = ref1; 1620 ref1 = ref2; 1621 ref2 = p; 1622 } 1623 1624 v1 = ref1->v; 1625 v2 = ref2->v; 1626 u1 = ref1->u; 1627 u2 = ref2->u; 1628 d1 = u1 - v1; 1629 d2 = u2 - v2; 1630 1631 if ( u1 == u2 || v1 == v2 ) 1632 { 1633 for ( p = p1; p <= p2; p++ ) 1634 { 1635 u = p->v; 1636 1637 if ( u <= v1 ) 1638 u += d1; 1639 else if ( u >= v2 ) 1640 u += d2; 1641 else 1642 u = u1; 1643 1644 p->u = u; 1645 } 1646 } 1647 else 1648 { 1649 FT_Fixed scale = FT_DivFix( u2 - u1, v2 - v1 ); 1650 1651 1652 for ( p = p1; p <= p2; p++ ) 1653 { 1654 u = p->v; 1655 1656 if ( u <= v1 ) 1657 u += d1; 1658 else if ( u >= v2 ) 1659 u += d2; 1660 else 1661 u = u1 + FT_MulFix( u - v1, scale ); 1662 1663 p->u = u; 1664 } 1665 } 1666 } 1667 1668 1669 /* Hint the weak points -- this is equivalent to the TrueType `IUP' */ 1670 /* hinting instruction. */ 1671 1672 FT_LOCAL_DEF( void ) af_glyph_hints_align_weak_points(AF_GlyphHints hints,AF_Dimension dim)1673 af_glyph_hints_align_weak_points( AF_GlyphHints hints, 1674 AF_Dimension dim ) 1675 { 1676 AF_Point points = hints->points; 1677 AF_Point point_limit = points + hints->num_points; 1678 AF_Point* contour = hints->contours; 1679 AF_Point* contour_limit = contour + hints->num_contours; 1680 FT_UInt touch_flag; 1681 AF_Point point; 1682 AF_Point end_point; 1683 AF_Point first_point; 1684 1685 1686 /* PASS 1: Move segment points to edge positions */ 1687 1688 if ( dim == AF_DIMENSION_HORZ ) 1689 { 1690 touch_flag = AF_FLAG_TOUCH_X; 1691 1692 for ( point = points; point < point_limit; point++ ) 1693 { 1694 point->u = point->x; 1695 point->v = point->ox; 1696 } 1697 } 1698 else 1699 { 1700 touch_flag = AF_FLAG_TOUCH_Y; 1701 1702 for ( point = points; point < point_limit; point++ ) 1703 { 1704 point->u = point->y; 1705 point->v = point->oy; 1706 } 1707 } 1708 1709 for ( ; contour < contour_limit; contour++ ) 1710 { 1711 AF_Point first_touched, last_touched; 1712 1713 1714 point = *contour; 1715 end_point = point->prev; 1716 first_point = point; 1717 1718 /* find first touched point */ 1719 for (;;) 1720 { 1721 if ( point > end_point ) /* no touched point in contour */ 1722 goto NextContour; 1723 1724 if ( point->flags & touch_flag ) 1725 break; 1726 1727 point++; 1728 } 1729 1730 first_touched = point; 1731 1732 for (;;) 1733 { 1734 FT_ASSERT( point <= end_point && 1735 ( point->flags & touch_flag ) != 0 ); 1736 1737 /* skip any touched neighbours */ 1738 while ( point < end_point && 1739 ( point[1].flags & touch_flag ) != 0 ) 1740 point++; 1741 1742 last_touched = point; 1743 1744 /* find the next touched point, if any */ 1745 point++; 1746 for (;;) 1747 { 1748 if ( point > end_point ) 1749 goto EndContour; 1750 1751 if ( ( point->flags & touch_flag ) != 0 ) 1752 break; 1753 1754 point++; 1755 } 1756 1757 /* interpolate between last_touched and point */ 1758 af_iup_interp( last_touched + 1, point - 1, 1759 last_touched, point ); 1760 } 1761 1762 EndContour: 1763 /* special case: only one point was touched */ 1764 if ( last_touched == first_touched ) 1765 af_iup_shift( first_point, end_point, first_touched ); 1766 1767 else /* interpolate the last part */ 1768 { 1769 if ( last_touched < end_point ) 1770 af_iup_interp( last_touched + 1, end_point, 1771 last_touched, first_touched ); 1772 1773 if ( first_touched > points ) 1774 af_iup_interp( first_point, first_touched - 1, 1775 last_touched, first_touched ); 1776 } 1777 1778 NextContour: 1779 ; 1780 } 1781 1782 /* now save the interpolated values back to x/y */ 1783 if ( dim == AF_DIMENSION_HORZ ) 1784 { 1785 for ( point = points; point < point_limit; point++ ) 1786 point->x = point->u; 1787 } 1788 else 1789 { 1790 for ( point = points; point < point_limit; point++ ) 1791 point->y = point->u; 1792 } 1793 } 1794 1795 1796 /* END */ 1797