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