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