1 /**************************************************************************** 2 * 3 * ftoutln.c 4 * 5 * FreeType outline management (body). 6 * 7 * Copyright (C) 1996-2019 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 <ft2build.h> 20 #include FT_OUTLINE_H 21 #include FT_INTERNAL_OBJECTS_H 22 #include FT_INTERNAL_CALC_H 23 #include FT_INTERNAL_DEBUG_H 24 #include FT_TRIGONOMETRY_H 25 26 27 /************************************************************************** 28 * 29 * The macro FT_COMPONENT is used in trace mode. It is an implicit 30 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 31 * messages during execution. 32 */ 33 #undef FT_COMPONENT 34 #define FT_COMPONENT outline 35 36 37 static 38 const FT_Outline null_outline = { 0, 0, NULL, NULL, NULL, 0 }; 39 40 41 /* documentation is in ftoutln.h */ 42 43 FT_EXPORT_DEF( FT_Error ) 44 FT_Outline_Decompose( FT_Outline* outline, 45 const FT_Outline_Funcs* func_interface, 46 void* user ) 47 { 48 #undef SCALED 49 #define SCALED( x ) ( ( (x) < 0 ? -( -(x) << shift ) \ 50 : ( (x) << shift ) ) - delta ) 51 52 FT_Vector v_last; 53 FT_Vector v_control; 54 FT_Vector v_start; 55 56 FT_Vector* point; 57 FT_Vector* limit; 58 char* tags; 59 60 FT_Error error; 61 62 FT_Int n; /* index of contour in outline */ 63 FT_UInt first; /* index of first point in contour */ 64 FT_Int tag; /* current point's state */ 65 66 FT_Int shift; 67 FT_Pos delta; 68 69 70 if ( !outline ) 71 return FT_THROW( Invalid_Outline ); 72 73 if ( !func_interface ) 74 return FT_THROW( Invalid_Argument ); 75 76 shift = func_interface->shift; 77 delta = func_interface->delta; 78 first = 0; 79 80 for ( n = 0; n < outline->n_contours; n++ ) 81 { 82 FT_Int last; /* index of last point in contour */ 83 84 85 FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n )); 86 87 last = outline->contours[n]; 88 if ( last < 0 ) 89 goto Invalid_Outline; 90 limit = outline->points + last; 91 92 v_start = outline->points[first]; 93 v_start.x = SCALED( v_start.x ); 94 v_start.y = SCALED( v_start.y ); 95 96 v_last = outline->points[last]; 97 v_last.x = SCALED( v_last.x ); 98 v_last.y = SCALED( v_last.y ); 99 100 v_control = v_start; 101 102 point = outline->points + first; 103 tags = outline->tags + first; 104 tag = FT_CURVE_TAG( tags[0] ); 105 106 /* A contour cannot start with a cubic control point! */ 107 if ( tag == FT_CURVE_TAG_CUBIC ) 108 goto Invalid_Outline; 109 110 /* check first point to determine origin */ 111 if ( tag == FT_CURVE_TAG_CONIC ) 112 { 113 /* first point is conic control. Yes, this happens. */ 114 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 115 { 116 /* start at last point if it is on the curve */ 117 v_start = v_last; 118 limit--; 119 } 120 else 121 { 122 /* if both first and last points are conic, */ 123 /* start at their middle and record its position */ 124 /* for closure */ 125 v_start.x = ( v_start.x + v_last.x ) / 2; 126 v_start.y = ( v_start.y + v_last.y ) / 2; 127 128 /* v_last = v_start; */ 129 } 130 point--; 131 tags--; 132 } 133 134 FT_TRACE5(( " move to (%.2f, %.2f)\n", 135 v_start.x / 64.0, v_start.y / 64.0 )); 136 error = func_interface->move_to( &v_start, user ); 137 if ( error ) 138 goto Exit; 139 140 while ( point < limit ) 141 { 142 point++; 143 tags++; 144 145 tag = FT_CURVE_TAG( tags[0] ); 146 switch ( tag ) 147 { 148 case FT_CURVE_TAG_ON: /* emit a single line_to */ 149 { 150 FT_Vector vec; 151 152 153 vec.x = SCALED( point->x ); 154 vec.y = SCALED( point->y ); 155 156 FT_TRACE5(( " line to (%.2f, %.2f)\n", 157 vec.x / 64.0, vec.y / 64.0 )); 158 error = func_interface->line_to( &vec, user ); 159 if ( error ) 160 goto Exit; 161 continue; 162 } 163 164 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 165 v_control.x = SCALED( point->x ); 166 v_control.y = SCALED( point->y ); 167 168 Do_Conic: 169 if ( point < limit ) 170 { 171 FT_Vector vec; 172 FT_Vector v_middle; 173 174 175 point++; 176 tags++; 177 tag = FT_CURVE_TAG( tags[0] ); 178 179 vec.x = SCALED( point->x ); 180 vec.y = SCALED( point->y ); 181 182 if ( tag == FT_CURVE_TAG_ON ) 183 { 184 FT_TRACE5(( " conic to (%.2f, %.2f)" 185 " with control (%.2f, %.2f)\n", 186 vec.x / 64.0, vec.y / 64.0, 187 v_control.x / 64.0, v_control.y / 64.0 )); 188 error = func_interface->conic_to( &v_control, &vec, user ); 189 if ( error ) 190 goto Exit; 191 continue; 192 } 193 194 if ( tag != FT_CURVE_TAG_CONIC ) 195 goto Invalid_Outline; 196 197 v_middle.x = ( v_control.x + vec.x ) / 2; 198 v_middle.y = ( v_control.y + vec.y ) / 2; 199 200 FT_TRACE5(( " conic to (%.2f, %.2f)" 201 " with control (%.2f, %.2f)\n", 202 v_middle.x / 64.0, v_middle.y / 64.0, 203 v_control.x / 64.0, v_control.y / 64.0 )); 204 error = func_interface->conic_to( &v_control, &v_middle, user ); 205 if ( error ) 206 goto Exit; 207 208 v_control = vec; 209 goto Do_Conic; 210 } 211 212 FT_TRACE5(( " conic to (%.2f, %.2f)" 213 " with control (%.2f, %.2f)\n", 214 v_start.x / 64.0, v_start.y / 64.0, 215 v_control.x / 64.0, v_control.y / 64.0 )); 216 error = func_interface->conic_to( &v_control, &v_start, user ); 217 goto Close; 218 219 default: /* FT_CURVE_TAG_CUBIC */ 220 { 221 FT_Vector vec1, vec2; 222 223 224 if ( point + 1 > limit || 225 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 226 goto Invalid_Outline; 227 228 point += 2; 229 tags += 2; 230 231 vec1.x = SCALED( point[-2].x ); 232 vec1.y = SCALED( point[-2].y ); 233 234 vec2.x = SCALED( point[-1].x ); 235 vec2.y = SCALED( point[-1].y ); 236 237 if ( point <= limit ) 238 { 239 FT_Vector vec; 240 241 242 vec.x = SCALED( point->x ); 243 vec.y = SCALED( point->y ); 244 245 FT_TRACE5(( " cubic to (%.2f, %.2f)" 246 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", 247 vec.x / 64.0, vec.y / 64.0, 248 vec1.x / 64.0, vec1.y / 64.0, 249 vec2.x / 64.0, vec2.y / 64.0 )); 250 error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); 251 if ( error ) 252 goto Exit; 253 continue; 254 } 255 256 FT_TRACE5(( " cubic to (%.2f, %.2f)" 257 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", 258 v_start.x / 64.0, v_start.y / 64.0, 259 vec1.x / 64.0, vec1.y / 64.0, 260 vec2.x / 64.0, vec2.y / 64.0 )); 261 error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); 262 goto Close; 263 } 264 } 265 } 266 267 /* close the contour with a line segment */ 268 FT_TRACE5(( " line to (%.2f, %.2f)\n", 269 v_start.x / 64.0, v_start.y / 64.0 )); 270 error = func_interface->line_to( &v_start, user ); 271 272 Close: 273 if ( error ) 274 goto Exit; 275 276 first = (FT_UInt)last + 1; 277 } 278 279 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); 280 return FT_Err_Ok; 281 282 Invalid_Outline: 283 error = FT_THROW( Invalid_Outline ); 284 /* fall through */ 285 286 Exit: 287 FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error )); 288 return error; 289 } 290 291 292 /* documentation is in ftoutln.h */ 293 294 FT_EXPORT_DEF( FT_Error ) 295 FT_Outline_New( FT_Library library, 296 FT_UInt numPoints, 297 FT_Int numContours, 298 FT_Outline *anoutline ) 299 { 300 FT_Error error; 301 FT_Memory memory; 302 303 304 if ( !library ) 305 return FT_THROW( Invalid_Library_Handle ); 306 307 memory = library->memory; 308 309 if ( !anoutline || !memory ) 310 return FT_THROW( Invalid_Argument ); 311 312 *anoutline = null_outline; 313 314 if ( numContours < 0 || 315 (FT_UInt)numContours > numPoints ) 316 return FT_THROW( Invalid_Argument ); 317 318 if ( numPoints > FT_OUTLINE_POINTS_MAX ) 319 return FT_THROW( Array_Too_Large ); 320 321 if ( FT_NEW_ARRAY( anoutline->points, numPoints ) || 322 FT_NEW_ARRAY( anoutline->tags, numPoints ) || 323 FT_NEW_ARRAY( anoutline->contours, numContours ) ) 324 goto Fail; 325 326 anoutline->n_points = (FT_Short)numPoints; 327 anoutline->n_contours = (FT_Short)numContours; 328 anoutline->flags |= FT_OUTLINE_OWNER; 329 330 return FT_Err_Ok; 331 332 Fail: 333 anoutline->flags |= FT_OUTLINE_OWNER; 334 FT_Outline_Done( library, anoutline ); 335 336 return error; 337 } 338 339 340 /* documentation is in ftoutln.h */ 341 342 FT_EXPORT_DEF( FT_Error ) 343 FT_Outline_Check( FT_Outline* outline ) 344 { 345 if ( outline ) 346 { 347 FT_Int n_points = outline->n_points; 348 FT_Int n_contours = outline->n_contours; 349 FT_Int end0, end; 350 FT_Int n; 351 352 353 /* empty glyph? */ 354 if ( n_points == 0 && n_contours == 0 ) 355 return FT_Err_Ok; 356 357 /* check point and contour counts */ 358 if ( n_points <= 0 || n_contours <= 0 ) 359 goto Bad; 360 361 end0 = end = -1; 362 for ( n = 0; n < n_contours; n++ ) 363 { 364 end = outline->contours[n]; 365 366 /* note that we don't accept empty contours */ 367 if ( end <= end0 || end >= n_points ) 368 goto Bad; 369 370 end0 = end; 371 } 372 373 if ( end != n_points - 1 ) 374 goto Bad; 375 376 /* XXX: check the tags array */ 377 return FT_Err_Ok; 378 } 379 380 Bad: 381 return FT_THROW( Invalid_Argument ); 382 } 383 384 385 /* documentation is in ftoutln.h */ 386 387 FT_EXPORT_DEF( FT_Error ) 388 FT_Outline_Copy( const FT_Outline* source, 389 FT_Outline *target ) 390 { 391 FT_Int is_owner; 392 393 394 if ( !source || !target ) 395 return FT_THROW( Invalid_Outline ); 396 397 if ( source->n_points != target->n_points || 398 source->n_contours != target->n_contours ) 399 return FT_THROW( Invalid_Argument ); 400 401 if ( source == target ) 402 return FT_Err_Ok; 403 404 if ( source->n_points ) 405 { 406 FT_ARRAY_COPY( target->points, source->points, source->n_points ); 407 FT_ARRAY_COPY( target->tags, source->tags, source->n_points ); 408 } 409 410 if ( source->n_contours ) 411 FT_ARRAY_COPY( target->contours, source->contours, source->n_contours ); 412 413 /* copy all flags, except the `FT_OUTLINE_OWNER' one */ 414 is_owner = target->flags & FT_OUTLINE_OWNER; 415 target->flags = source->flags; 416 417 target->flags &= ~FT_OUTLINE_OWNER; 418 target->flags |= is_owner; 419 420 return FT_Err_Ok; 421 } 422 423 424 /* documentation is in ftoutln.h */ 425 426 FT_EXPORT_DEF( FT_Error ) 427 FT_Outline_Done( FT_Library library, 428 FT_Outline* outline ) 429 { 430 FT_Memory memory; 431 432 433 if ( !library ) 434 return FT_THROW( Invalid_Library_Handle ); 435 436 if ( !outline ) 437 return FT_THROW( Invalid_Outline ); 438 439 memory = library->memory; 440 441 if ( !memory ) 442 return FT_THROW( Invalid_Argument ); 443 444 if ( outline->flags & FT_OUTLINE_OWNER ) 445 { 446 FT_FREE( outline->points ); 447 FT_FREE( outline->tags ); 448 FT_FREE( outline->contours ); 449 } 450 *outline = null_outline; 451 452 return FT_Err_Ok; 453 } 454 455 456 /* documentation is in ftoutln.h */ 457 458 FT_EXPORT_DEF( void ) 459 FT_Outline_Get_CBox( const FT_Outline* outline, 460 FT_BBox *acbox ) 461 { 462 FT_Pos xMin, yMin, xMax, yMax; 463 464 465 if ( outline && acbox ) 466 { 467 if ( outline->n_points == 0 ) 468 { 469 xMin = 0; 470 yMin = 0; 471 xMax = 0; 472 yMax = 0; 473 } 474 else 475 { 476 FT_Vector* vec = outline->points; 477 FT_Vector* limit = vec + outline->n_points; 478 479 480 xMin = xMax = vec->x; 481 yMin = yMax = vec->y; 482 vec++; 483 484 for ( ; vec < limit; vec++ ) 485 { 486 FT_Pos x, y; 487 488 489 x = vec->x; 490 if ( x < xMin ) xMin = x; 491 if ( x > xMax ) xMax = x; 492 493 y = vec->y; 494 if ( y < yMin ) yMin = y; 495 if ( y > yMax ) yMax = y; 496 } 497 } 498 acbox->xMin = xMin; 499 acbox->xMax = xMax; 500 acbox->yMin = yMin; 501 acbox->yMax = yMax; 502 } 503 } 504 505 506 /* documentation is in ftoutln.h */ 507 508 FT_EXPORT_DEF( void ) 509 FT_Outline_Translate( const FT_Outline* outline, 510 FT_Pos xOffset, 511 FT_Pos yOffset ) 512 { 513 FT_UShort n; 514 FT_Vector* vec; 515 516 517 if ( !outline ) 518 return; 519 520 vec = outline->points; 521 522 for ( n = 0; n < outline->n_points; n++ ) 523 { 524 vec->x = ADD_LONG( vec->x, xOffset ); 525 vec->y = ADD_LONG( vec->y, yOffset ); 526 vec++; 527 } 528 } 529 530 531 /* documentation is in ftoutln.h */ 532 533 FT_EXPORT_DEF( void ) 534 FT_Outline_Reverse( FT_Outline* outline ) 535 { 536 FT_UShort n; 537 FT_Int first, last; 538 539 540 if ( !outline ) 541 return; 542 543 first = 0; 544 545 for ( n = 0; n < outline->n_contours; n++ ) 546 { 547 last = outline->contours[n]; 548 549 /* reverse point table */ 550 { 551 FT_Vector* p = outline->points + first; 552 FT_Vector* q = outline->points + last; 553 FT_Vector swap; 554 555 556 while ( p < q ) 557 { 558 swap = *p; 559 *p = *q; 560 *q = swap; 561 p++; 562 q--; 563 } 564 } 565 566 /* reverse tags table */ 567 { 568 char* p = outline->tags + first; 569 char* q = outline->tags + last; 570 571 572 while ( p < q ) 573 { 574 char swap; 575 576 577 swap = *p; 578 *p = *q; 579 *q = swap; 580 p++; 581 q--; 582 } 583 } 584 585 first = last + 1; 586 } 587 588 outline->flags ^= FT_OUTLINE_REVERSE_FILL; 589 } 590 591 592 /* documentation is in ftoutln.h */ 593 594 FT_EXPORT_DEF( FT_Error ) 595 FT_Outline_Render( FT_Library library, 596 FT_Outline* outline, 597 FT_Raster_Params* params ) 598 { 599 FT_Error error; 600 FT_Renderer renderer; 601 FT_ListNode node; 602 FT_BBox cbox; 603 604 605 if ( !library ) 606 return FT_THROW( Invalid_Library_Handle ); 607 608 if ( !outline ) 609 return FT_THROW( Invalid_Outline ); 610 611 if ( !params ) 612 return FT_THROW( Invalid_Argument ); 613 614 FT_Outline_Get_CBox( outline, &cbox ); 615 if ( cbox.xMin < -0x1000000L || cbox.yMin < -0x1000000L || 616 cbox.xMax > 0x1000000L || cbox.yMax > 0x1000000L ) 617 return FT_THROW( Invalid_Outline ); 618 619 renderer = library->cur_renderer; 620 node = library->renderers.head; 621 622 params->source = (void*)outline; 623 624 error = FT_ERR( Cannot_Render_Glyph ); 625 while ( renderer ) 626 { 627 error = renderer->raster_render( renderer->raster, params ); 628 if ( !error || FT_ERR_NEQ( error, Cannot_Render_Glyph ) ) 629 break; 630 631 /* FT_Err_Cannot_Render_Glyph is returned if the render mode */ 632 /* is unsupported by the current renderer for this glyph image */ 633 /* format */ 634 635 /* now, look for another renderer that supports the same */ 636 /* format */ 637 renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE, 638 &node ); 639 } 640 641 return error; 642 } 643 644 645 /* documentation is in ftoutln.h */ 646 647 FT_EXPORT_DEF( FT_Error ) 648 FT_Outline_Get_Bitmap( FT_Library library, 649 FT_Outline* outline, 650 const FT_Bitmap *abitmap ) 651 { 652 FT_Raster_Params params; 653 654 655 if ( !abitmap ) 656 return FT_THROW( Invalid_Argument ); 657 658 /* other checks are delayed to `FT_Outline_Render' */ 659 660 params.target = abitmap; 661 params.flags = 0; 662 663 if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY || 664 abitmap->pixel_mode == FT_PIXEL_MODE_LCD || 665 abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V ) 666 params.flags |= FT_RASTER_FLAG_AA; 667 668 return FT_Outline_Render( library, outline, ¶ms ); 669 } 670 671 672 /* documentation is in freetype.h */ 673 674 FT_EXPORT_DEF( void ) 675 FT_Vector_Transform( FT_Vector* vector, 676 const FT_Matrix* matrix ) 677 { 678 FT_Pos xz, yz; 679 680 681 if ( !vector || !matrix ) 682 return; 683 684 xz = FT_MulFix( vector->x, matrix->xx ) + 685 FT_MulFix( vector->y, matrix->xy ); 686 687 yz = FT_MulFix( vector->x, matrix->yx ) + 688 FT_MulFix( vector->y, matrix->yy ); 689 690 vector->x = xz; 691 vector->y = yz; 692 } 693 694 695 /* documentation is in ftoutln.h */ 696 697 FT_EXPORT_DEF( void ) 698 FT_Outline_Transform( const FT_Outline* outline, 699 const FT_Matrix* matrix ) 700 { 701 FT_Vector* vec; 702 FT_Vector* limit; 703 704 705 if ( !outline || !matrix ) 706 return; 707 708 vec = outline->points; 709 limit = vec + outline->n_points; 710 711 for ( ; vec < limit; vec++ ) 712 FT_Vector_Transform( vec, matrix ); 713 } 714 715 716 #if 0 717 718 #define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \ 719 do \ 720 { \ 721 (first) = ( c > 0 ) ? (outline)->points + \ 722 (outline)->contours[c - 1] + 1 \ 723 : (outline)->points; \ 724 (last) = (outline)->points + (outline)->contours[c]; \ 725 } while ( 0 ) 726 727 728 /* Is a point in some contour? */ 729 /* */ 730 /* We treat every point of the contour as if it */ 731 /* it were ON. That is, we allow false positives, */ 732 /* but disallow false negatives. (XXX really?) */ 733 static FT_Bool 734 ft_contour_has( FT_Outline* outline, 735 FT_Short c, 736 FT_Vector* point ) 737 { 738 FT_Vector* first; 739 FT_Vector* last; 740 FT_Vector* a; 741 FT_Vector* b; 742 FT_UInt n = 0; 743 744 745 FT_OUTLINE_GET_CONTOUR( outline, c, first, last ); 746 747 for ( a = first; a <= last; a++ ) 748 { 749 FT_Pos x; 750 FT_Int intersect; 751 752 753 b = ( a == last ) ? first : a + 1; 754 755 intersect = ( a->y - point->y ) ^ ( b->y - point->y ); 756 757 /* a and b are on the same side */ 758 if ( intersect >= 0 ) 759 { 760 if ( intersect == 0 && a->y == point->y ) 761 { 762 if ( ( a->x <= point->x && b->x >= point->x ) || 763 ( a->x >= point->x && b->x <= point->x ) ) 764 return 1; 765 } 766 767 continue; 768 } 769 770 x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y ); 771 772 if ( x < point->x ) 773 n++; 774 else if ( x == point->x ) 775 return 1; 776 } 777 778 return n & 1; 779 } 780 781 782 static FT_Bool 783 ft_contour_enclosed( FT_Outline* outline, 784 FT_UShort c ) 785 { 786 FT_Vector* first; 787 FT_Vector* last; 788 FT_Short i; 789 790 791 FT_OUTLINE_GET_CONTOUR( outline, c, first, last ); 792 793 for ( i = 0; i < outline->n_contours; i++ ) 794 { 795 if ( i != c && ft_contour_has( outline, i, first ) ) 796 { 797 FT_Vector* pt; 798 799 800 for ( pt = first + 1; pt <= last; pt++ ) 801 if ( !ft_contour_has( outline, i, pt ) ) 802 return 0; 803 804 return 1; 805 } 806 } 807 808 return 0; 809 } 810 811 812 /* This version differs from the public one in that each */ 813 /* part (contour not enclosed in another contour) of the */ 814 /* outline is checked for orientation. This is */ 815 /* necessary for some buggy CJK fonts. */ 816 static FT_Orientation 817 ft_outline_get_orientation( FT_Outline* outline ) 818 { 819 FT_Short i; 820 FT_Vector* first; 821 FT_Vector* last; 822 FT_Orientation orient = FT_ORIENTATION_NONE; 823 824 825 first = outline->points; 826 for ( i = 0; i < outline->n_contours; i++, first = last + 1 ) 827 { 828 FT_Vector* point; 829 FT_Vector* xmin_point; 830 FT_Pos xmin; 831 832 833 last = outline->points + outline->contours[i]; 834 835 /* skip degenerate contours */ 836 if ( last < first + 2 ) 837 continue; 838 839 if ( ft_contour_enclosed( outline, i ) ) 840 continue; 841 842 xmin = first->x; 843 xmin_point = first; 844 845 for ( point = first + 1; point <= last; point++ ) 846 { 847 if ( point->x < xmin ) 848 { 849 xmin = point->x; 850 xmin_point = point; 851 } 852 } 853 854 /* check the orientation of the contour */ 855 { 856 FT_Vector* prev; 857 FT_Vector* next; 858 FT_Orientation o; 859 860 861 prev = ( xmin_point == first ) ? last : xmin_point - 1; 862 next = ( xmin_point == last ) ? first : xmin_point + 1; 863 864 if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) > 865 FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) ) 866 o = FT_ORIENTATION_POSTSCRIPT; 867 else 868 o = FT_ORIENTATION_TRUETYPE; 869 870 if ( orient == FT_ORIENTATION_NONE ) 871 orient = o; 872 else if ( orient != o ) 873 return FT_ORIENTATION_NONE; 874 } 875 } 876 877 return orient; 878 } 879 880 #endif /* 0 */ 881 882 883 /* documentation is in ftoutln.h */ 884 885 FT_EXPORT_DEF( FT_Error ) 886 FT_Outline_Embolden( FT_Outline* outline, 887 FT_Pos strength ) 888 { 889 return FT_Outline_EmboldenXY( outline, strength, strength ); 890 } 891 892 893 /* documentation is in ftoutln.h */ 894 895 FT_EXPORT_DEF( FT_Error ) 896 FT_Outline_EmboldenXY( FT_Outline* outline, 897 FT_Pos xstrength, 898 FT_Pos ystrength ) 899 { 900 FT_Vector* points; 901 FT_Int c, first, last; 902 FT_Orientation orientation; 903 904 905 if ( !outline ) 906 return FT_THROW( Invalid_Outline ); 907 908 xstrength /= 2; 909 ystrength /= 2; 910 if ( xstrength == 0 && ystrength == 0 ) 911 return FT_Err_Ok; 912 913 orientation = FT_Outline_Get_Orientation( outline ); 914 if ( orientation == FT_ORIENTATION_NONE ) 915 { 916 if ( outline->n_contours ) 917 return FT_THROW( Invalid_Argument ); 918 else 919 return FT_Err_Ok; 920 } 921 922 points = outline->points; 923 924 first = 0; 925 for ( c = 0; c < outline->n_contours; c++ ) 926 { 927 FT_Vector in, out, anchor, shift; 928 FT_Fixed l_in, l_out, l_anchor = 0, l, q, d; 929 FT_Int i, j, k; 930 931 932 l_in = 0; 933 last = outline->contours[c]; 934 935 /* pacify compiler */ 936 in.x = in.y = anchor.x = anchor.y = 0; 937 938 /* Counter j cycles though the points; counter i advances only */ 939 /* when points are moved; anchor k marks the first moved point. */ 940 for ( i = last, j = first, k = -1; 941 j != i && i != k; 942 j = j < last ? j + 1 : first ) 943 { 944 if ( j != k ) 945 { 946 out.x = points[j].x - points[i].x; 947 out.y = points[j].y - points[i].y; 948 l_out = (FT_Fixed)FT_Vector_NormLen( &out ); 949 950 if ( l_out == 0 ) 951 continue; 952 } 953 else 954 { 955 out = anchor; 956 l_out = l_anchor; 957 } 958 959 if ( l_in != 0 ) 960 { 961 if ( k < 0 ) 962 { 963 k = i; 964 anchor = in; 965 l_anchor = l_in; 966 } 967 968 d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y ); 969 970 /* shift only if turn is less than ~160 degrees */ 971 if ( d > -0xF000L ) 972 { 973 d = d + 0x10000L; 974 975 /* shift components along lateral bisector in proper orientation */ 976 shift.x = in.y + out.y; 977 shift.y = in.x + out.x; 978 979 if ( orientation == FT_ORIENTATION_TRUETYPE ) 980 shift.x = -shift.x; 981 else 982 shift.y = -shift.y; 983 984 /* restrict shift magnitude to better handle collapsing segments */ 985 q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x ); 986 if ( orientation == FT_ORIENTATION_TRUETYPE ) 987 q = -q; 988 989 l = FT_MIN( l_in, l_out ); 990 991 /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ 992 if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) ) 993 shift.x = FT_MulDiv( shift.x, xstrength, d ); 994 else 995 shift.x = FT_MulDiv( shift.x, l, q ); 996 997 998 if ( FT_MulFix( ystrength, q ) <= FT_MulFix( l, d ) ) 999 shift.y = FT_MulDiv( shift.y, ystrength, d ); 1000 else 1001 shift.y = FT_MulDiv( shift.y, l, q ); 1002 } 1003 else 1004 shift.x = shift.y = 0; 1005 1006 for ( ; 1007 i != j; 1008 i = i < last ? i + 1 : first ) 1009 { 1010 points[i].x += xstrength + shift.x; 1011 points[i].y += ystrength + shift.y; 1012 } 1013 } 1014 else 1015 i = j; 1016 1017 in = out; 1018 l_in = l_out; 1019 } 1020 1021 first = last + 1; 1022 } 1023 1024 return FT_Err_Ok; 1025 } 1026 1027 1028 /* documentation is in ftoutln.h */ 1029 1030 FT_EXPORT_DEF( FT_Orientation ) 1031 FT_Outline_Get_Orientation( FT_Outline* outline ) 1032 { 1033 FT_BBox cbox = { 0, 0, 0, 0 }; 1034 FT_Int xshift, yshift; 1035 FT_Vector* points; 1036 FT_Vector v_prev, v_cur; 1037 FT_Int c, n, first; 1038 FT_Pos area = 0; 1039 1040 1041 if ( !outline || outline->n_points <= 0 ) 1042 return FT_ORIENTATION_TRUETYPE; 1043 1044 /* We use the nonzero winding rule to find the orientation. */ 1045 /* Since glyph outlines behave much more `regular' than arbitrary */ 1046 /* cubic or quadratic curves, this test deals with the polygon */ 1047 /* only that is spanned up by the control points. */ 1048 1049 FT_Outline_Get_CBox( outline, &cbox ); 1050 1051 /* Handle collapsed outlines to avoid undefined FT_MSB. */ 1052 if ( cbox.xMin == cbox.xMax || cbox.yMin == cbox.yMax ) 1053 return FT_ORIENTATION_NONE; 1054 1055 xshift = FT_MSB( (FT_UInt32)( FT_ABS( cbox.xMax ) | 1056 FT_ABS( cbox.xMin ) ) ) - 14; 1057 xshift = FT_MAX( xshift, 0 ); 1058 1059 yshift = FT_MSB( (FT_UInt32)( cbox.yMax - cbox.yMin ) ) - 14; 1060 yshift = FT_MAX( yshift, 0 ); 1061 1062 points = outline->points; 1063 1064 first = 0; 1065 for ( c = 0; c < outline->n_contours; c++ ) 1066 { 1067 FT_Int last = outline->contours[c]; 1068 1069 1070 v_prev.x = points[last].x >> xshift; 1071 v_prev.y = points[last].y >> yshift; 1072 1073 for ( n = first; n <= last; n++ ) 1074 { 1075 v_cur.x = points[n].x >> xshift; 1076 v_cur.y = points[n].y >> yshift; 1077 1078 area = ADD_LONG( area, 1079 MUL_LONG( v_cur.y - v_prev.y, 1080 v_cur.x + v_prev.x ) ); 1081 1082 v_prev = v_cur; 1083 } 1084 1085 first = last + 1; 1086 } 1087 1088 if ( area > 0 ) 1089 return FT_ORIENTATION_POSTSCRIPT; 1090 else if ( area < 0 ) 1091 return FT_ORIENTATION_TRUETYPE; 1092 else 1093 return FT_ORIENTATION_NONE; 1094 } 1095 1096 1097 /* END */