1 /***************************************************************************/ 2 /* */ 3 /* afhints.c */ 4 /* */ 5 /* Auto-fitter hinting routines (body). */ 6 /* */ 7 /* Copyright 2003-2007, 2009-2011 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 23 24 /* Get new segment for given axis. */ 25 26 FT_LOCAL_DEF( FT_Error ) af_axis_hints_new_segment(AF_AxisHints axis,FT_Memory memory,AF_Segment * asegment)27 af_axis_hints_new_segment( AF_AxisHints axis, 28 FT_Memory memory, 29 AF_Segment *asegment ) 30 { 31 FT_Error error = AF_Err_Ok; 32 AF_Segment segment = NULL; 33 34 35 if ( axis->num_segments >= axis->max_segments ) 36 { 37 FT_Int old_max = axis->max_segments; 38 FT_Int new_max = old_max; 39 FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *segment ) ); 40 41 42 if ( old_max >= big_max ) 43 { 44 error = AF_Err_Out_Of_Memory; 45 goto Exit; 46 } 47 48 new_max += ( new_max >> 2 ) + 4; 49 if ( new_max < old_max || new_max > big_max ) 50 new_max = big_max; 51 52 if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) ) 53 goto Exit; 54 55 axis->max_segments = new_max; 56 } 57 58 segment = axis->segments + axis->num_segments++; 59 60 Exit: 61 *asegment = segment; 62 return error; 63 } 64 65 66 /* Get new edge for given axis, direction, and position. */ 67 68 FT_LOCAL( FT_Error ) af_axis_hints_new_edge(AF_AxisHints axis,FT_Int fpos,AF_Direction dir,FT_Memory memory,AF_Edge * aedge)69 af_axis_hints_new_edge( AF_AxisHints axis, 70 FT_Int fpos, 71 AF_Direction dir, 72 FT_Memory memory, 73 AF_Edge *aedge ) 74 { 75 FT_Error error = AF_Err_Ok; 76 AF_Edge edge = NULL; 77 AF_Edge edges; 78 79 80 if ( axis->num_edges >= axis->max_edges ) 81 { 82 FT_Int old_max = axis->max_edges; 83 FT_Int new_max = old_max; 84 FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *edge ) ); 85 86 87 if ( old_max >= big_max ) 88 { 89 error = AF_Err_Out_Of_Memory; 90 goto Exit; 91 } 92 93 new_max += ( new_max >> 2 ) + 4; 94 if ( new_max < old_max || new_max > big_max ) 95 new_max = big_max; 96 97 if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) ) 98 goto Exit; 99 100 axis->max_edges = new_max; 101 } 102 103 edges = axis->edges; 104 edge = edges + axis->num_edges; 105 106 while ( edge > edges ) 107 { 108 if ( edge[-1].fpos < fpos ) 109 break; 110 111 /* we want the edge with same position and minor direction */ 112 /* to appear before those in the major one in the list */ 113 if ( edge[-1].fpos == fpos && dir == axis->major_dir ) 114 break; 115 116 edge[0] = edge[-1]; 117 edge--; 118 } 119 120 axis->num_edges++; 121 122 FT_ZERO( edge ); 123 edge->fpos = (FT_Short)fpos; 124 edge->dir = (FT_Char)dir; 125 126 Exit: 127 *aedge = edge; 128 return error; 129 } 130 131 132 #ifdef FT_DEBUG_AUTOFIT 133 134 #include FT_CONFIG_STANDARD_LIBRARY_H 135 136 static const char* af_dir_str(AF_Direction dir)137 af_dir_str( AF_Direction dir ) 138 { 139 const char* result; 140 141 142 switch ( dir ) 143 { 144 case AF_DIR_UP: 145 result = "up"; 146 break; 147 case AF_DIR_DOWN: 148 result = "down"; 149 break; 150 case AF_DIR_LEFT: 151 result = "left"; 152 break; 153 case AF_DIR_RIGHT: 154 result = "right"; 155 break; 156 default: 157 result = "none"; 158 } 159 160 return result; 161 } 162 163 164 #define AF_INDEX_NUM( ptr, base ) ( (ptr) ? ( (ptr) - (base) ) : -1 ) 165 166 167 #ifdef __cplusplus 168 extern "C" { 169 #endif 170 void af_glyph_hints_dump_points(AF_GlyphHints hints)171 af_glyph_hints_dump_points( AF_GlyphHints hints ) 172 { 173 AF_Point points = hints->points; 174 AF_Point limit = points + hints->num_points; 175 AF_Point point; 176 177 178 printf( "Table of points:\n" ); 179 printf( " [ index | xorg | yorg | xscale | yscale" 180 " | xfit | yfit | flags ]\n" ); 181 182 for ( point = points; point < limit; point++ ) 183 { 184 printf( " [ %5d | %5d | %5d | %6.2f | %6.2f" 185 " | %5.2f | %5.2f | %c%c%c%c%c%c ]\n", 186 point - points, 187 point->fx, 188 point->fy, 189 point->ox / 64.0, 190 point->oy / 64.0, 191 point->x / 64.0, 192 point->y / 64.0, 193 ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ? 'w' : ' ', 194 ( point->flags & AF_FLAG_INFLECTION ) ? 'i' : ' ', 195 ( point->flags & AF_FLAG_EXTREMA_X ) ? '<' : ' ', 196 ( point->flags & AF_FLAG_EXTREMA_Y ) ? 'v' : ' ', 197 ( point->flags & AF_FLAG_ROUND_X ) ? '(' : ' ', 198 ( point->flags & AF_FLAG_ROUND_Y ) ? 'u' : ' '); 199 } 200 printf( "\n" ); 201 } 202 #ifdef __cplusplus 203 } 204 #endif 205 206 207 static const char* af_edge_flags_to_string(AF_Edge_Flags flags)208 af_edge_flags_to_string( AF_Edge_Flags flags ) 209 { 210 static char temp[32]; 211 int pos = 0; 212 213 214 if ( flags & AF_EDGE_ROUND ) 215 { 216 ft_memcpy( temp + pos, "round", 5 ); 217 pos += 5; 218 } 219 if ( flags & AF_EDGE_SERIF ) 220 { 221 if ( pos > 0 ) 222 temp[pos++] = ' '; 223 ft_memcpy( temp + pos, "serif", 5 ); 224 pos += 5; 225 } 226 if ( pos == 0 ) 227 return "normal"; 228 229 temp[pos] = 0; 230 231 return temp; 232 } 233 234 235 /* Dump the array of linked segments. */ 236 237 #ifdef __cplusplus 238 extern "C" { 239 #endif 240 void af_glyph_hints_dump_segments(AF_GlyphHints hints)241 af_glyph_hints_dump_segments( AF_GlyphHints hints ) 242 { 243 FT_Int dimension; 244 245 246 for ( dimension = 1; dimension >= 0; dimension-- ) 247 { 248 AF_AxisHints axis = &hints->axis[dimension]; 249 AF_Segment segments = axis->segments; 250 AF_Segment limit = segments + axis->num_segments; 251 AF_Segment seg; 252 253 254 printf ( "Table of %s segments:\n", 255 dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" ); 256 printf ( " [ index | pos | dir | link | serif |" 257 " height | extra | flags ]\n" ); 258 259 for ( seg = segments; seg < limit; seg++ ) 260 { 261 printf ( " [ %5d | %5.2g | %5s | %4d | %5d | %6d | %5d | %11s ]\n", 262 seg - segments, 263 dimension == AF_DIMENSION_HORZ ? (int)seg->first->ox / 64.0 264 : (int)seg->first->oy / 64.0, 265 af_dir_str( (AF_Direction)seg->dir ), 266 AF_INDEX_NUM( seg->link, segments ), 267 AF_INDEX_NUM( seg->serif, segments ), 268 seg->height, 269 seg->height - ( seg->max_coord - seg->min_coord ), 270 af_edge_flags_to_string( (AF_Edge_Flags)seg->flags ) ); 271 } 272 printf( "\n" ); 273 } 274 } 275 #ifdef __cplusplus 276 } 277 #endif 278 279 280 /* Fetch number of segments. */ 281 282 #ifdef __cplusplus 283 extern "C" { 284 #endif 285 FT_Error af_glyph_hints_get_num_segments(AF_GlyphHints hints,FT_Int dimension,FT_Int * num_segments)286 af_glyph_hints_get_num_segments( AF_GlyphHints hints, 287 FT_Int dimension, 288 FT_Int* num_segments ) 289 { 290 AF_Dimension dim; 291 AF_AxisHints axis; 292 293 294 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT; 295 296 axis = &hints->axis[dim]; 297 *num_segments = axis->num_segments; 298 299 return AF_Err_Ok; 300 } 301 #ifdef __cplusplus 302 } 303 #endif 304 305 306 /* Fetch offset of segments into user supplied offset array. */ 307 308 #ifdef __cplusplus 309 extern "C" { 310 #endif 311 FT_Error af_glyph_hints_get_segment_offset(AF_GlyphHints hints,FT_Int dimension,FT_Int idx,FT_Pos * offset)312 af_glyph_hints_get_segment_offset( AF_GlyphHints hints, 313 FT_Int dimension, 314 FT_Int idx, 315 FT_Pos* offset ) 316 { 317 AF_Dimension dim; 318 AF_AxisHints axis; 319 AF_Segment seg; 320 321 322 if ( !offset ) 323 return AF_Err_Invalid_Argument; 324 325 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT; 326 327 axis = &hints->axis[dim]; 328 329 if ( idx < 0 || idx >= axis->num_segments ) 330 return AF_Err_Invalid_Argument; 331 332 seg = &axis->segments[idx]; 333 *offset = (dim == AF_DIMENSION_HORZ) ? seg->first->ox 334 : seg->first->oy; 335 336 return AF_Err_Ok; 337 } 338 #ifdef __cplusplus 339 } 340 #endif 341 342 343 /* Dump the array of linked edges. */ 344 345 #ifdef __cplusplus 346 extern "C" { 347 #endif 348 void af_glyph_hints_dump_edges(AF_GlyphHints hints)349 af_glyph_hints_dump_edges( AF_GlyphHints hints ) 350 { 351 FT_Int dimension; 352 353 354 for ( dimension = 1; dimension >= 0; dimension-- ) 355 { 356 AF_AxisHints axis = &hints->axis[dimension]; 357 AF_Edge edges = axis->edges; 358 AF_Edge limit = edges + axis->num_edges; 359 AF_Edge edge; 360 361 362 /* 363 * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges 364 * since they have a constant X coordinate. 365 */ 366 printf ( "Table of %s edges:\n", 367 dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" ); 368 printf ( " [ index | pos | dir | link |" 369 " serif | blue | opos | pos | flags ]\n" ); 370 371 for ( edge = edges; edge < limit; edge++ ) 372 { 373 printf ( " [ %5d | %5.2g | %5s | %4d |" 374 " %5d | %c | %5.2f | %5.2f | %11s ]\n", 375 edge - edges, 376 (int)edge->opos / 64.0, 377 af_dir_str( (AF_Direction)edge->dir ), 378 AF_INDEX_NUM( edge->link, edges ), 379 AF_INDEX_NUM( edge->serif, edges ), 380 edge->blue_edge ? 'y' : 'n', 381 edge->opos / 64.0, 382 edge->pos / 64.0, 383 af_edge_flags_to_string( (AF_Edge_Flags)edge->flags ) ); 384 } 385 printf( "\n" ); 386 } 387 } 388 #ifdef __cplusplus 389 } 390 #endif 391 392 #else /* !FT_DEBUG_AUTOFIT */ 393 394 /* these empty stubs are only used to link the `ftgrid' test program */ 395 /* if debugging is disabled */ 396 397 #ifdef __cplusplus 398 extern "C" { 399 #endif 400 401 void af_glyph_hints_dump_points(AF_GlyphHints hints)402 af_glyph_hints_dump_points( AF_GlyphHints hints ) 403 { 404 FT_UNUSED( hints ); 405 } 406 407 408 void af_glyph_hints_dump_segments(AF_GlyphHints hints)409 af_glyph_hints_dump_segments( AF_GlyphHints hints ) 410 { 411 FT_UNUSED( hints ); 412 } 413 414 415 FT_Error af_glyph_hints_get_num_segments(AF_GlyphHints hints,FT_Int dimension,FT_Int * num_segments)416 af_glyph_hints_get_num_segments( AF_GlyphHints hints, 417 FT_Int dimension, 418 FT_Int* num_segments ) 419 { 420 FT_UNUSED( hints ); 421 FT_UNUSED( dimension ); 422 FT_UNUSED( num_segments ); 423 424 return 0; 425 } 426 427 428 FT_Error af_glyph_hints_get_segment_offset(AF_GlyphHints hints,FT_Int dimension,FT_Int idx,FT_Pos * offset)429 af_glyph_hints_get_segment_offset( AF_GlyphHints hints, 430 FT_Int dimension, 431 FT_Int idx, 432 FT_Pos* offset ) 433 { 434 FT_UNUSED( hints ); 435 FT_UNUSED( dimension ); 436 FT_UNUSED( idx ); 437 FT_UNUSED( offset ); 438 439 return 0; 440 } 441 442 443 void af_glyph_hints_dump_edges(AF_GlyphHints hints)444 af_glyph_hints_dump_edges( AF_GlyphHints hints ) 445 { 446 FT_UNUSED( hints ); 447 } 448 449 #ifdef __cplusplus 450 } 451 #endif 452 453 #endif /* !FT_DEBUG_AUTOFIT */ 454 455 456 /* Compute the direction value of a given vector. */ 457 458 FT_LOCAL_DEF( AF_Direction ) af_direction_compute(FT_Pos dx,FT_Pos dy)459 af_direction_compute( FT_Pos dx, 460 FT_Pos dy ) 461 { 462 FT_Pos ll, ss; /* long and short arm lengths */ 463 AF_Direction dir; /* candidate direction */ 464 465 466 if ( dy >= dx ) 467 { 468 if ( dy >= -dx ) 469 { 470 dir = AF_DIR_UP; 471 ll = dy; 472 ss = dx; 473 } 474 else 475 { 476 dir = AF_DIR_LEFT; 477 ll = -dx; 478 ss = dy; 479 } 480 } 481 else /* dy < dx */ 482 { 483 if ( dy >= -dx ) 484 { 485 dir = AF_DIR_RIGHT; 486 ll = dx; 487 ss = dy; 488 } 489 else 490 { 491 dir = AF_DIR_DOWN; 492 ll = dy; 493 ss = dx; 494 } 495 } 496 497 /* return no direction if arm lengths differ too much */ 498 /* (value 14 is heuristic) */ 499 ss *= 14; 500 if ( FT_ABS( ll ) <= FT_ABS( ss ) ) 501 dir = AF_DIR_NONE; 502 503 return dir; 504 } 505 506 507 FT_LOCAL_DEF( void ) af_glyph_hints_init(AF_GlyphHints hints,FT_Memory memory)508 af_glyph_hints_init( AF_GlyphHints hints, 509 FT_Memory memory ) 510 { 511 FT_ZERO( hints ); 512 hints->memory = memory; 513 } 514 515 516 FT_LOCAL_DEF( void ) af_glyph_hints_done(AF_GlyphHints hints)517 af_glyph_hints_done( AF_GlyphHints hints ) 518 { 519 if ( hints && hints->memory ) 520 { 521 FT_Memory memory = hints->memory; 522 int dim; 523 524 525 /* 526 * note that we don't need to free the segment and edge 527 * buffers since they are really within the hints->points array 528 */ 529 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ ) 530 { 531 AF_AxisHints axis = &hints->axis[dim]; 532 533 534 axis->num_segments = 0; 535 axis->max_segments = 0; 536 FT_FREE( axis->segments ); 537 538 axis->num_edges = 0; 539 axis->max_edges = 0; 540 FT_FREE( axis->edges ); 541 } 542 543 FT_FREE( hints->contours ); 544 hints->max_contours = 0; 545 hints->num_contours = 0; 546 547 FT_FREE( hints->points ); 548 hints->num_points = 0; 549 hints->max_points = 0; 550 551 hints->memory = NULL; 552 } 553 } 554 555 556 /* Reset metrics. */ 557 558 FT_LOCAL_DEF( void ) af_glyph_hints_rescale(AF_GlyphHints hints,AF_ScriptMetrics metrics)559 af_glyph_hints_rescale( AF_GlyphHints hints, 560 AF_ScriptMetrics metrics ) 561 { 562 hints->metrics = metrics; 563 hints->scaler_flags = metrics->scaler.flags; 564 } 565 566 567 /* Recompute all AF_Point in AF_GlyphHints from the definitions */ 568 /* in a source outline. */ 569 570 FT_LOCAL_DEF( FT_Error ) af_glyph_hints_reload(AF_GlyphHints hints,FT_Outline * outline)571 af_glyph_hints_reload( AF_GlyphHints hints, 572 FT_Outline* outline ) 573 { 574 FT_Error error = AF_Err_Ok; 575 AF_Point points; 576 FT_UInt old_max, new_max; 577 FT_Fixed x_scale = hints->x_scale; 578 FT_Fixed y_scale = hints->y_scale; 579 FT_Pos x_delta = hints->x_delta; 580 FT_Pos y_delta = hints->y_delta; 581 FT_Memory memory = hints->memory; 582 583 584 hints->num_points = 0; 585 hints->num_contours = 0; 586 587 hints->axis[0].num_segments = 0; 588 hints->axis[0].num_edges = 0; 589 hints->axis[1].num_segments = 0; 590 hints->axis[1].num_edges = 0; 591 592 /* first of all, reallocate the contours array if necessary */ 593 new_max = (FT_UInt)outline->n_contours; 594 old_max = hints->max_contours; 595 if ( new_max > old_max ) 596 { 597 new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */ 598 599 if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) 600 goto Exit; 601 602 hints->max_contours = new_max; 603 } 604 605 /* 606 * then reallocate the points arrays if necessary -- 607 * note that we reserve two additional point positions, used to 608 * hint metrics appropriately 609 */ 610 new_max = (FT_UInt)( outline->n_points + 2 ); 611 old_max = hints->max_points; 612 if ( new_max > old_max ) 613 { 614 new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */ 615 616 if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) ) 617 goto Exit; 618 619 hints->max_points = new_max; 620 } 621 622 hints->num_points = outline->n_points; 623 hints->num_contours = outline->n_contours; 624 625 /* We can't rely on the value of `FT_Outline.flags' to know the fill */ 626 /* direction used for a glyph, given that some fonts are broken (e.g., */ 627 /* the Arphic ones). We thus recompute it each time we need to. */ 628 /* */ 629 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP; 630 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT; 631 632 if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT ) 633 { 634 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN; 635 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT; 636 } 637 638 hints->x_scale = x_scale; 639 hints->y_scale = y_scale; 640 hints->x_delta = x_delta; 641 hints->y_delta = y_delta; 642 643 hints->xmin_delta = 0; 644 hints->xmax_delta = 0; 645 646 points = hints->points; 647 if ( hints->num_points == 0 ) 648 goto Exit; 649 650 { 651 AF_Point point; 652 AF_Point point_limit = points + hints->num_points; 653 654 655 /* compute coordinates & Bezier flags, next and prev */ 656 { 657 FT_Vector* vec = outline->points; 658 char* tag = outline->tags; 659 AF_Point end = points + outline->contours[0]; 660 AF_Point prev = end; 661 FT_Int contour_index = 0; 662 663 664 for ( point = points; point < point_limit; point++, vec++, tag++ ) 665 { 666 point->fx = (FT_Short)vec->x; 667 point->fy = (FT_Short)vec->y; 668 point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta; 669 point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta; 670 671 switch ( FT_CURVE_TAG( *tag ) ) 672 { 673 case FT_CURVE_TAG_CONIC: 674 point->flags = AF_FLAG_CONIC; 675 break; 676 case FT_CURVE_TAG_CUBIC: 677 point->flags = AF_FLAG_CUBIC; 678 break; 679 default: 680 point->flags = AF_FLAG_NONE; 681 } 682 683 point->prev = prev; 684 prev->next = point; 685 prev = point; 686 687 if ( point == end ) 688 { 689 if ( ++contour_index < outline->n_contours ) 690 { 691 end = points + outline->contours[contour_index]; 692 prev = end; 693 } 694 } 695 } 696 } 697 698 /* set up the contours array */ 699 { 700 AF_Point* contour = hints->contours; 701 AF_Point* contour_limit = contour + hints->num_contours; 702 short* end = outline->contours; 703 short idx = 0; 704 705 706 for ( ; contour < contour_limit; contour++, end++ ) 707 { 708 contour[0] = points + idx; 709 idx = (short)( end[0] + 1 ); 710 } 711 } 712 713 /* compute directions of in & out vectors */ 714 { 715 AF_Point first = points; 716 AF_Point prev = NULL; 717 FT_Pos in_x = 0; 718 FT_Pos in_y = 0; 719 AF_Direction in_dir = AF_DIR_NONE; 720 721 722 for ( point = points; point < point_limit; point++ ) 723 { 724 AF_Point next; 725 FT_Pos out_x, out_y; 726 727 728 if ( point == first ) 729 { 730 prev = first->prev; 731 in_x = first->fx - prev->fx; 732 in_y = first->fy - prev->fy; 733 in_dir = af_direction_compute( in_x, in_y ); 734 first = prev + 1; 735 } 736 737 point->in_dir = (FT_Char)in_dir; 738 739 next = point->next; 740 out_x = next->fx - point->fx; 741 out_y = next->fy - point->fy; 742 743 in_dir = af_direction_compute( out_x, out_y ); 744 point->out_dir = (FT_Char)in_dir; 745 746 /* check for weak points */ 747 748 if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) ) 749 { 750 Is_Weak_Point: 751 point->flags |= AF_FLAG_WEAK_INTERPOLATION; 752 } 753 else if ( point->out_dir == point->in_dir ) 754 { 755 if ( point->out_dir != AF_DIR_NONE ) 756 goto Is_Weak_Point; 757 758 if ( ft_corner_is_flat( in_x, in_y, out_x, out_y ) ) 759 goto Is_Weak_Point; 760 } 761 else if ( point->in_dir == -point->out_dir ) 762 goto Is_Weak_Point; 763 764 in_x = out_x; 765 in_y = out_y; 766 prev = point; 767 } 768 } 769 } 770 771 Exit: 772 return error; 773 } 774 775 776 /* Store the hinted outline in an FT_Outline structure. */ 777 778 FT_LOCAL_DEF( void ) af_glyph_hints_save(AF_GlyphHints hints,FT_Outline * outline)779 af_glyph_hints_save( AF_GlyphHints hints, 780 FT_Outline* outline ) 781 { 782 AF_Point point = hints->points; 783 AF_Point limit = point + hints->num_points; 784 FT_Vector* vec = outline->points; 785 char* tag = outline->tags; 786 787 788 for ( ; point < limit; point++, vec++, tag++ ) 789 { 790 vec->x = point->x; 791 vec->y = point->y; 792 793 if ( point->flags & AF_FLAG_CONIC ) 794 tag[0] = FT_CURVE_TAG_CONIC; 795 else if ( point->flags & AF_FLAG_CUBIC ) 796 tag[0] = FT_CURVE_TAG_CUBIC; 797 else 798 tag[0] = FT_CURVE_TAG_ON; 799 } 800 } 801 802 803 /**************************************************************** 804 * 805 * EDGE POINT GRID-FITTING 806 * 807 ****************************************************************/ 808 809 810 /* Align all points of an edge to the same coordinate value, */ 811 /* either horizontally or vertically. */ 812 813 FT_LOCAL_DEF( void ) af_glyph_hints_align_edge_points(AF_GlyphHints hints,AF_Dimension dim)814 af_glyph_hints_align_edge_points( AF_GlyphHints hints, 815 AF_Dimension dim ) 816 { 817 AF_AxisHints axis = & hints->axis[dim]; 818 AF_Segment segments = axis->segments; 819 AF_Segment segment_limit = segments + axis->num_segments; 820 AF_Segment seg; 821 822 823 if ( dim == AF_DIMENSION_HORZ ) 824 { 825 for ( seg = segments; seg < segment_limit; seg++ ) 826 { 827 AF_Edge edge = seg->edge; 828 AF_Point point, first, last; 829 830 831 if ( edge == NULL ) 832 continue; 833 834 first = seg->first; 835 last = seg->last; 836 point = first; 837 for (;;) 838 { 839 point->x = edge->pos; 840 point->flags |= AF_FLAG_TOUCH_X; 841 842 if ( point == last ) 843 break; 844 845 point = point->next; 846 } 847 } 848 } 849 else 850 { 851 for ( seg = segments; seg < segment_limit; seg++ ) 852 { 853 AF_Edge edge = seg->edge; 854 AF_Point point, first, last; 855 856 857 if ( edge == NULL ) 858 continue; 859 860 first = seg->first; 861 last = seg->last; 862 point = first; 863 for (;;) 864 { 865 point->y = edge->pos; 866 point->flags |= AF_FLAG_TOUCH_Y; 867 868 if ( point == last ) 869 break; 870 871 point = point->next; 872 } 873 } 874 } 875 } 876 877 878 /**************************************************************** 879 * 880 * STRONG POINT INTERPOLATION 881 * 882 ****************************************************************/ 883 884 885 /* Hint the strong points -- this is equivalent to the TrueType `IP' */ 886 /* hinting instruction. */ 887 888 FT_LOCAL_DEF( void ) af_glyph_hints_align_strong_points(AF_GlyphHints hints,AF_Dimension dim)889 af_glyph_hints_align_strong_points( AF_GlyphHints hints, 890 AF_Dimension dim ) 891 { 892 AF_Point points = hints->points; 893 AF_Point point_limit = points + hints->num_points; 894 AF_AxisHints axis = &hints->axis[dim]; 895 AF_Edge edges = axis->edges; 896 AF_Edge edge_limit = edges + axis->num_edges; 897 AF_Flags touch_flag; 898 899 900 if ( dim == AF_DIMENSION_HORZ ) 901 touch_flag = AF_FLAG_TOUCH_X; 902 else 903 touch_flag = AF_FLAG_TOUCH_Y; 904 905 if ( edges < edge_limit ) 906 { 907 AF_Point point; 908 AF_Edge edge; 909 910 911 for ( point = points; point < point_limit; point++ ) 912 { 913 FT_Pos u, ou, fu; /* point position */ 914 FT_Pos delta; 915 916 917 if ( point->flags & touch_flag ) 918 continue; 919 920 /* if this point is candidate to weak interpolation, we */ 921 /* interpolate it after all strong points have been processed */ 922 923 if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) && 924 !( point->flags & AF_FLAG_INFLECTION ) ) 925 continue; 926 927 if ( dim == AF_DIMENSION_VERT ) 928 { 929 u = point->fy; 930 ou = point->oy; 931 } 932 else 933 { 934 u = point->fx; 935 ou = point->ox; 936 } 937 938 fu = u; 939 940 /* is the point before the first edge? */ 941 edge = edges; 942 delta = edge->fpos - u; 943 if ( delta >= 0 ) 944 { 945 u = edge->pos - ( edge->opos - ou ); 946 goto Store_Point; 947 } 948 949 /* is the point after the last edge? */ 950 edge = edge_limit - 1; 951 delta = u - edge->fpos; 952 if ( delta >= 0 ) 953 { 954 u = edge->pos + ( ou - edge->opos ); 955 goto Store_Point; 956 } 957 958 { 959 FT_PtrDist min, max, mid; 960 FT_Pos fpos; 961 962 963 /* find enclosing edges */ 964 min = 0; 965 max = edge_limit - edges; 966 967 #if 1 968 /* for a small number of edges, a linear search is better */ 969 if ( max <= 8 ) 970 { 971 FT_PtrDist nn; 972 973 974 for ( nn = 0; nn < max; nn++ ) 975 if ( edges[nn].fpos >= u ) 976 break; 977 978 if ( edges[nn].fpos == u ) 979 { 980 u = edges[nn].pos; 981 goto Store_Point; 982 } 983 min = nn; 984 } 985 else 986 #endif 987 while ( min < max ) 988 { 989 mid = ( max + min ) >> 1; 990 edge = edges + mid; 991 fpos = edge->fpos; 992 993 if ( u < fpos ) 994 max = mid; 995 else if ( u > fpos ) 996 min = mid + 1; 997 else 998 { 999 /* we are on the edge */ 1000 u = edge->pos; 1001 goto Store_Point; 1002 } 1003 } 1004 1005 /* point is not on an edge */ 1006 { 1007 AF_Edge before = edges + min - 1; 1008 AF_Edge after = edges + min + 0; 1009 1010 1011 /* assert( before && after && before != after ) */ 1012 if ( before->scale == 0 ) 1013 before->scale = FT_DivFix( after->pos - before->pos, 1014 after->fpos - before->fpos ); 1015 1016 u = before->pos + FT_MulFix( fu - before->fpos, 1017 before->scale ); 1018 } 1019 } 1020 1021 Store_Point: 1022 /* save the point position */ 1023 if ( dim == AF_DIMENSION_HORZ ) 1024 point->x = u; 1025 else 1026 point->y = u; 1027 1028 point->flags |= touch_flag; 1029 } 1030 } 1031 } 1032 1033 1034 /**************************************************************** 1035 * 1036 * WEAK POINT INTERPOLATION 1037 * 1038 ****************************************************************/ 1039 1040 1041 /* Shift the original coordinates of all points between `p1' and */ 1042 /* `p2' to get hinted coordinates, using the same difference as */ 1043 /* given by `ref'. */ 1044 1045 static void af_iup_shift(AF_Point p1,AF_Point p2,AF_Point ref)1046 af_iup_shift( AF_Point p1, 1047 AF_Point p2, 1048 AF_Point ref ) 1049 { 1050 AF_Point p; 1051 FT_Pos delta = ref->u - ref->v; 1052 1053 1054 if ( delta == 0 ) 1055 return; 1056 1057 for ( p = p1; p < ref; p++ ) 1058 p->u = p->v + delta; 1059 1060 for ( p = ref + 1; p <= p2; p++ ) 1061 p->u = p->v + delta; 1062 } 1063 1064 1065 /* Interpolate the original coordinates of all points between `p1' and */ 1066 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */ 1067 /* reference points. The `u' and `v' members are the current and */ 1068 /* original coordinate values, respectively. */ 1069 /* */ 1070 /* Details can be found in the TrueType bytecode specification. */ 1071 1072 static void af_iup_interp(AF_Point p1,AF_Point p2,AF_Point ref1,AF_Point ref2)1073 af_iup_interp( AF_Point p1, 1074 AF_Point p2, 1075 AF_Point ref1, 1076 AF_Point ref2 ) 1077 { 1078 AF_Point p; 1079 FT_Pos u; 1080 FT_Pos v1 = ref1->v; 1081 FT_Pos v2 = ref2->v; 1082 FT_Pos d1 = ref1->u - v1; 1083 FT_Pos d2 = ref2->u - v2; 1084 1085 1086 if ( p1 > p2 ) 1087 return; 1088 1089 if ( v1 == v2 ) 1090 { 1091 for ( p = p1; p <= p2; p++ ) 1092 { 1093 u = p->v; 1094 1095 if ( u <= v1 ) 1096 u += d1; 1097 else 1098 u += d2; 1099 1100 p->u = u; 1101 } 1102 return; 1103 } 1104 1105 if ( v1 < v2 ) 1106 { 1107 for ( p = p1; p <= p2; p++ ) 1108 { 1109 u = p->v; 1110 1111 if ( u <= v1 ) 1112 u += d1; 1113 else if ( u >= v2 ) 1114 u += d2; 1115 else 1116 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); 1117 1118 p->u = u; 1119 } 1120 } 1121 else 1122 { 1123 for ( p = p1; p <= p2; p++ ) 1124 { 1125 u = p->v; 1126 1127 if ( u <= v2 ) 1128 u += d2; 1129 else if ( u >= v1 ) 1130 u += d1; 1131 else 1132 u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 ); 1133 1134 p->u = u; 1135 } 1136 } 1137 } 1138 1139 1140 /* Hint the weak points -- this is equivalent to the TrueType `IUP' */ 1141 /* hinting instruction. */ 1142 1143 FT_LOCAL_DEF( void ) af_glyph_hints_align_weak_points(AF_GlyphHints hints,AF_Dimension dim)1144 af_glyph_hints_align_weak_points( AF_GlyphHints hints, 1145 AF_Dimension dim ) 1146 { 1147 AF_Point points = hints->points; 1148 AF_Point point_limit = points + hints->num_points; 1149 AF_Point* contour = hints->contours; 1150 AF_Point* contour_limit = contour + hints->num_contours; 1151 AF_Flags touch_flag; 1152 AF_Point point; 1153 AF_Point end_point; 1154 AF_Point first_point; 1155 1156 1157 /* PASS 1: Move segment points to edge positions */ 1158 1159 if ( dim == AF_DIMENSION_HORZ ) 1160 { 1161 touch_flag = AF_FLAG_TOUCH_X; 1162 1163 for ( point = points; point < point_limit; point++ ) 1164 { 1165 point->u = point->x; 1166 point->v = point->ox; 1167 } 1168 } 1169 else 1170 { 1171 touch_flag = AF_FLAG_TOUCH_Y; 1172 1173 for ( point = points; point < point_limit; point++ ) 1174 { 1175 point->u = point->y; 1176 point->v = point->oy; 1177 } 1178 } 1179 1180 point = points; 1181 1182 for ( ; contour < contour_limit; contour++ ) 1183 { 1184 AF_Point first_touched, last_touched; 1185 1186 1187 point = *contour; 1188 end_point = point->prev; 1189 first_point = point; 1190 1191 /* find first touched point */ 1192 for (;;) 1193 { 1194 if ( point > end_point ) /* no touched point in contour */ 1195 goto NextContour; 1196 1197 if ( point->flags & touch_flag ) 1198 break; 1199 1200 point++; 1201 } 1202 1203 first_touched = point; 1204 last_touched = point; 1205 1206 for (;;) 1207 { 1208 FT_ASSERT( point <= end_point && 1209 ( point->flags & touch_flag ) != 0 ); 1210 1211 /* skip any touched neighbours */ 1212 while ( point < end_point && 1213 ( point[1].flags & touch_flag ) != 0 ) 1214 point++; 1215 1216 last_touched = point; 1217 1218 /* find the next touched point, if any */ 1219 point++; 1220 for (;;) 1221 { 1222 if ( point > end_point ) 1223 goto EndContour; 1224 1225 if ( ( point->flags & touch_flag ) != 0 ) 1226 break; 1227 1228 point++; 1229 } 1230 1231 /* interpolate between last_touched and point */ 1232 af_iup_interp( last_touched + 1, point - 1, 1233 last_touched, point ); 1234 } 1235 1236 EndContour: 1237 /* special case: only one point was touched */ 1238 if ( last_touched == first_touched ) 1239 af_iup_shift( first_point, end_point, first_touched ); 1240 1241 else /* interpolate the last part */ 1242 { 1243 if ( last_touched < end_point ) 1244 af_iup_interp( last_touched + 1, end_point, 1245 last_touched, first_touched ); 1246 1247 if ( first_touched > points ) 1248 af_iup_interp( first_point, first_touched - 1, 1249 last_touched, first_touched ); 1250 } 1251 1252 NextContour: 1253 ; 1254 } 1255 1256 /* now save the interpolated values back to x/y */ 1257 if ( dim == AF_DIMENSION_HORZ ) 1258 { 1259 for ( point = points; point < point_limit; point++ ) 1260 point->x = point->u; 1261 } 1262 else 1263 { 1264 for ( point = points; point < point_limit; point++ ) 1265 point->y = point->u; 1266 } 1267 } 1268 1269 1270 #ifdef AF_CONFIG_OPTION_USE_WARPER 1271 1272 /* Apply (small) warp scale and warp delta for given dimension. */ 1273 1274 FT_LOCAL_DEF( void ) af_glyph_hints_scale_dim(AF_GlyphHints hints,AF_Dimension dim,FT_Fixed scale,FT_Pos delta)1275 af_glyph_hints_scale_dim( AF_GlyphHints hints, 1276 AF_Dimension dim, 1277 FT_Fixed scale, 1278 FT_Pos delta ) 1279 { 1280 AF_Point points = hints->points; 1281 AF_Point points_limit = points + hints->num_points; 1282 AF_Point point; 1283 1284 1285 if ( dim == AF_DIMENSION_HORZ ) 1286 { 1287 for ( point = points; point < points_limit; point++ ) 1288 point->x = FT_MulFix( point->fx, scale ) + delta; 1289 } 1290 else 1291 { 1292 for ( point = points; point < points_limit; point++ ) 1293 point->y = FT_MulFix( point->fy, scale ) + delta; 1294 } 1295 } 1296 1297 #endif /* AF_CONFIG_OPTION_USE_WARPER */ 1298 1299 /* END */ 1300