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