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