1 /***************************************************************************/ 2 /* */ 3 /* ftstroke.c */ 4 /* */ 5 /* FreeType path stroker (body). */ 6 /* */ 7 /* Copyright 2002-2015 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_STROKER_H 21 #include FT_TRIGONOMETRY_H 22 #include FT_OUTLINE_H 23 #include FT_INTERNAL_MEMORY_H 24 #include FT_INTERNAL_DEBUG_H 25 #include FT_INTERNAL_OBJECTS_H 26 27 28 /* documentation is in ftstroke.h */ 29 30 FT_EXPORT_DEF( FT_StrokerBorder ) 31 FT_Outline_GetInsideBorder( FT_Outline* outline ) 32 { 33 FT_Orientation o = FT_Outline_Get_Orientation( outline ); 34 35 36 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT 37 : FT_STROKER_BORDER_LEFT; 38 } 39 40 41 /* documentation is in ftstroke.h */ 42 43 FT_EXPORT_DEF( FT_StrokerBorder ) 44 FT_Outline_GetOutsideBorder( FT_Outline* outline ) 45 { 46 FT_Orientation o = FT_Outline_Get_Orientation( outline ); 47 48 49 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT 50 : FT_STROKER_BORDER_RIGHT; 51 } 52 53 54 /*************************************************************************/ 55 /*************************************************************************/ 56 /***** *****/ 57 /***** BEZIER COMPUTATIONS *****/ 58 /***** *****/ 59 /*************************************************************************/ 60 /*************************************************************************/ 61 62 #define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 ) 63 #define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 8 ) 64 65 #define FT_EPSILON 2 66 67 #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON ) 68 69 70 static FT_Pos 71 ft_pos_abs( FT_Pos x ) 72 { 73 return x >= 0 ? x : -x; 74 } 75 76 77 static void 78 ft_conic_split( FT_Vector* base ) 79 { 80 FT_Pos a, b; 81 82 83 base[4].x = base[2].x; 84 b = base[1].x; 85 a = base[3].x = ( base[2].x + b ) / 2; 86 b = base[1].x = ( base[0].x + b ) / 2; 87 base[2].x = ( a + b ) / 2; 88 89 base[4].y = base[2].y; 90 b = base[1].y; 91 a = base[3].y = ( base[2].y + b ) / 2; 92 b = base[1].y = ( base[0].y + b ) / 2; 93 base[2].y = ( a + b ) / 2; 94 } 95 96 97 static FT_Bool 98 ft_conic_is_small_enough( FT_Vector* base, 99 FT_Angle *angle_in, 100 FT_Angle *angle_out ) 101 { 102 FT_Vector d1, d2; 103 FT_Angle theta; 104 FT_Int close1, close2; 105 106 107 d1.x = base[1].x - base[2].x; 108 d1.y = base[1].y - base[2].y; 109 d2.x = base[0].x - base[1].x; 110 d2.y = base[0].y - base[1].y; 111 112 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 113 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 114 115 if ( close1 ) 116 { 117 if ( close2 ) 118 { 119 /* basically a point; */ 120 /* do nothing to retain original direction */ 121 } 122 else 123 { 124 *angle_in = 125 *angle_out = FT_Atan2( d2.x, d2.y ); 126 } 127 } 128 else /* !close1 */ 129 { 130 if ( close2 ) 131 { 132 *angle_in = 133 *angle_out = FT_Atan2( d1.x, d1.y ); 134 } 135 else 136 { 137 *angle_in = FT_Atan2( d1.x, d1.y ); 138 *angle_out = FT_Atan2( d2.x, d2.y ); 139 } 140 } 141 142 theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) ); 143 144 return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD ); 145 } 146 147 148 static void 149 ft_cubic_split( FT_Vector* base ) 150 { 151 FT_Pos a, b, c, d; 152 153 154 base[6].x = base[3].x; 155 c = base[1].x; 156 d = base[2].x; 157 base[1].x = a = ( base[0].x + c ) / 2; 158 base[5].x = b = ( base[3].x + d ) / 2; 159 c = ( c + d ) / 2; 160 base[2].x = a = ( a + c ) / 2; 161 base[4].x = b = ( b + c ) / 2; 162 base[3].x = ( a + b ) / 2; 163 164 base[6].y = base[3].y; 165 c = base[1].y; 166 d = base[2].y; 167 base[1].y = a = ( base[0].y + c ) / 2; 168 base[5].y = b = ( base[3].y + d ) / 2; 169 c = ( c + d ) / 2; 170 base[2].y = a = ( a + c ) / 2; 171 base[4].y = b = ( b + c ) / 2; 172 base[3].y = ( a + b ) / 2; 173 } 174 175 176 /* Return the average of `angle1' and `angle2'. */ 177 /* This gives correct result even if `angle1' and `angle2' */ 178 /* have opposite signs. */ 179 static FT_Angle 180 ft_angle_mean( FT_Angle angle1, 181 FT_Angle angle2 ) 182 { 183 return angle1 + FT_Angle_Diff( angle1, angle2 ) / 2; 184 } 185 186 187 static FT_Bool 188 ft_cubic_is_small_enough( FT_Vector* base, 189 FT_Angle *angle_in, 190 FT_Angle *angle_mid, 191 FT_Angle *angle_out ) 192 { 193 FT_Vector d1, d2, d3; 194 FT_Angle theta1, theta2; 195 FT_Int close1, close2, close3; 196 197 198 d1.x = base[2].x - base[3].x; 199 d1.y = base[2].y - base[3].y; 200 d2.x = base[1].x - base[2].x; 201 d2.y = base[1].y - base[2].y; 202 d3.x = base[0].x - base[1].x; 203 d3.y = base[0].y - base[1].y; 204 205 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 206 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 207 close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y ); 208 209 if ( close1 ) 210 { 211 if ( close2 ) 212 { 213 if ( close3 ) 214 { 215 /* basically a point; */ 216 /* do nothing to retain original direction */ 217 } 218 else /* !close3 */ 219 { 220 *angle_in = 221 *angle_mid = 222 *angle_out = FT_Atan2( d3.x, d3.y ); 223 } 224 } 225 else /* !close2 */ 226 { 227 if ( close3 ) 228 { 229 *angle_in = 230 *angle_mid = 231 *angle_out = FT_Atan2( d2.x, d2.y ); 232 } 233 else /* !close3 */ 234 { 235 *angle_in = 236 *angle_mid = FT_Atan2( d2.x, d2.y ); 237 *angle_out = FT_Atan2( d3.x, d3.y ); 238 } 239 } 240 } 241 else /* !close1 */ 242 { 243 if ( close2 ) 244 { 245 if ( close3 ) 246 { 247 *angle_in = 248 *angle_mid = 249 *angle_out = FT_Atan2( d1.x, d1.y ); 250 } 251 else /* !close3 */ 252 { 253 *angle_in = FT_Atan2( d1.x, d1.y ); 254 *angle_out = FT_Atan2( d3.x, d3.y ); 255 *angle_mid = ft_angle_mean( *angle_in, *angle_out ); 256 } 257 } 258 else /* !close2 */ 259 { 260 if ( close3 ) 261 { 262 *angle_in = FT_Atan2( d1.x, d1.y ); 263 *angle_mid = 264 *angle_out = FT_Atan2( d2.x, d2.y ); 265 } 266 else /* !close3 */ 267 { 268 *angle_in = FT_Atan2( d1.x, d1.y ); 269 *angle_mid = FT_Atan2( d2.x, d2.y ); 270 *angle_out = FT_Atan2( d3.x, d3.y ); 271 } 272 } 273 } 274 275 theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) ); 276 theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) ); 277 278 return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD && 279 theta2 < FT_SMALL_CUBIC_THRESHOLD ); 280 } 281 282 283 /*************************************************************************/ 284 /*************************************************************************/ 285 /***** *****/ 286 /***** STROKE BORDERS *****/ 287 /***** *****/ 288 /*************************************************************************/ 289 /*************************************************************************/ 290 291 typedef enum FT_StrokeTags_ 292 { 293 FT_STROKE_TAG_ON = 1, /* on-curve point */ 294 FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ 295 FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ 296 FT_STROKE_TAG_END = 8 /* sub-path end */ 297 298 } FT_StrokeTags; 299 300 #define FT_STROKE_TAG_BEGIN_END ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ) 301 302 typedef struct FT_StrokeBorderRec_ 303 { 304 FT_UInt num_points; 305 FT_UInt max_points; 306 FT_Vector* points; 307 FT_Byte* tags; 308 FT_Bool movable; /* TRUE for ends of lineto borders */ 309 FT_Int start; /* index of current sub-path start point */ 310 FT_Memory memory; 311 FT_Bool valid; 312 313 } FT_StrokeBorderRec, *FT_StrokeBorder; 314 315 316 static FT_Error 317 ft_stroke_border_grow( FT_StrokeBorder border, 318 FT_UInt new_points ) 319 { 320 FT_UInt old_max = border->max_points; 321 FT_UInt new_max = border->num_points + new_points; 322 FT_Error error = FT_Err_Ok; 323 324 325 if ( new_max > old_max ) 326 { 327 FT_UInt cur_max = old_max; 328 FT_Memory memory = border->memory; 329 330 331 while ( cur_max < new_max ) 332 cur_max += ( cur_max >> 1 ) + 16; 333 334 if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) || 335 FT_RENEW_ARRAY( border->tags, old_max, cur_max ) ) 336 goto Exit; 337 338 border->max_points = cur_max; 339 } 340 341 Exit: 342 return error; 343 } 344 345 346 static void 347 ft_stroke_border_close( FT_StrokeBorder border, 348 FT_Bool reverse ) 349 { 350 FT_UInt start = (FT_UInt)border->start; 351 FT_UInt count = border->num_points; 352 353 354 FT_ASSERT( border->start >= 0 ); 355 356 /* don't record empty paths! */ 357 if ( count <= start + 1U ) 358 border->num_points = start; 359 else 360 { 361 /* copy the last point to the start of this sub-path, since */ 362 /* it contains the `adjusted' starting coordinates */ 363 border->num_points = --count; 364 border->points[start] = border->points[count]; 365 366 if ( reverse ) 367 { 368 /* reverse the points */ 369 { 370 FT_Vector* vec1 = border->points + start + 1; 371 FT_Vector* vec2 = border->points + count - 1; 372 373 374 for ( ; vec1 < vec2; vec1++, vec2-- ) 375 { 376 FT_Vector tmp; 377 378 379 tmp = *vec1; 380 *vec1 = *vec2; 381 *vec2 = tmp; 382 } 383 } 384 385 /* then the tags */ 386 { 387 FT_Byte* tag1 = border->tags + start + 1; 388 FT_Byte* tag2 = border->tags + count - 1; 389 390 391 for ( ; tag1 < tag2; tag1++, tag2-- ) 392 { 393 FT_Byte tmp; 394 395 396 tmp = *tag1; 397 *tag1 = *tag2; 398 *tag2 = tmp; 399 } 400 } 401 } 402 403 border->tags[start ] |= FT_STROKE_TAG_BEGIN; 404 border->tags[count - 1] |= FT_STROKE_TAG_END; 405 } 406 407 border->start = -1; 408 border->movable = FALSE; 409 } 410 411 412 static FT_Error 413 ft_stroke_border_lineto( FT_StrokeBorder border, 414 FT_Vector* to, 415 FT_Bool movable ) 416 { 417 FT_Error error = FT_Err_Ok; 418 419 420 FT_ASSERT( border->start >= 0 ); 421 422 if ( border->movable ) 423 { 424 /* move last point */ 425 border->points[border->num_points - 1] = *to; 426 } 427 else 428 { 429 /* don't add zero-length lineto */ 430 if ( border->num_points > 0 && 431 FT_IS_SMALL( border->points[border->num_points - 1].x - to->x ) && 432 FT_IS_SMALL( border->points[border->num_points - 1].y - to->y ) ) 433 return error; 434 435 /* add one point */ 436 error = ft_stroke_border_grow( border, 1 ); 437 if ( !error ) 438 { 439 FT_Vector* vec = border->points + border->num_points; 440 FT_Byte* tag = border->tags + border->num_points; 441 442 443 vec[0] = *to; 444 tag[0] = FT_STROKE_TAG_ON; 445 446 border->num_points += 1; 447 } 448 } 449 border->movable = movable; 450 return error; 451 } 452 453 454 static FT_Error 455 ft_stroke_border_conicto( FT_StrokeBorder border, 456 FT_Vector* control, 457 FT_Vector* to ) 458 { 459 FT_Error error; 460 461 462 FT_ASSERT( border->start >= 0 ); 463 464 error = ft_stroke_border_grow( border, 2 ); 465 if ( !error ) 466 { 467 FT_Vector* vec = border->points + border->num_points; 468 FT_Byte* tag = border->tags + border->num_points; 469 470 471 vec[0] = *control; 472 vec[1] = *to; 473 474 tag[0] = 0; 475 tag[1] = FT_STROKE_TAG_ON; 476 477 border->num_points += 2; 478 } 479 480 border->movable = FALSE; 481 482 return error; 483 } 484 485 486 static FT_Error 487 ft_stroke_border_cubicto( FT_StrokeBorder border, 488 FT_Vector* control1, 489 FT_Vector* control2, 490 FT_Vector* to ) 491 { 492 FT_Error error; 493 494 495 FT_ASSERT( border->start >= 0 ); 496 497 error = ft_stroke_border_grow( border, 3 ); 498 if ( !error ) 499 { 500 FT_Vector* vec = border->points + border->num_points; 501 FT_Byte* tag = border->tags + border->num_points; 502 503 504 vec[0] = *control1; 505 vec[1] = *control2; 506 vec[2] = *to; 507 508 tag[0] = FT_STROKE_TAG_CUBIC; 509 tag[1] = FT_STROKE_TAG_CUBIC; 510 tag[2] = FT_STROKE_TAG_ON; 511 512 border->num_points += 3; 513 } 514 515 border->movable = FALSE; 516 517 return error; 518 } 519 520 521 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 ) 522 523 524 static FT_Error 525 ft_stroke_border_arcto( FT_StrokeBorder border, 526 FT_Vector* center, 527 FT_Fixed radius, 528 FT_Angle angle_start, 529 FT_Angle angle_diff ) 530 { 531 FT_Angle total, angle, step, rotate, next, theta; 532 FT_Vector a, b, a2, b2; 533 FT_Fixed length; 534 FT_Error error = FT_Err_Ok; 535 536 537 /* compute start point */ 538 FT_Vector_From_Polar( &a, radius, angle_start ); 539 a.x += center->x; 540 a.y += center->y; 541 542 total = angle_diff; 543 angle = angle_start; 544 rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2; 545 546 while ( total != 0 ) 547 { 548 step = total; 549 if ( step > FT_ARC_CUBIC_ANGLE ) 550 step = FT_ARC_CUBIC_ANGLE; 551 552 else if ( step < -FT_ARC_CUBIC_ANGLE ) 553 step = -FT_ARC_CUBIC_ANGLE; 554 555 next = angle + step; 556 theta = step; 557 if ( theta < 0 ) 558 theta = -theta; 559 560 theta >>= 1; 561 562 /* compute end point */ 563 FT_Vector_From_Polar( &b, radius, next ); 564 b.x += center->x; 565 b.y += center->y; 566 567 /* compute first and second control points */ 568 length = FT_MulDiv( radius, FT_Sin( theta ) * 4, 569 ( 0x10000L + FT_Cos( theta ) ) * 3 ); 570 571 FT_Vector_From_Polar( &a2, length, angle + rotate ); 572 a2.x += a.x; 573 a2.y += a.y; 574 575 FT_Vector_From_Polar( &b2, length, next - rotate ); 576 b2.x += b.x; 577 b2.y += b.y; 578 579 /* add cubic arc */ 580 error = ft_stroke_border_cubicto( border, &a2, &b2, &b ); 581 if ( error ) 582 break; 583 584 /* process the rest of the arc ?? */ 585 a = b; 586 total -= step; 587 angle = next; 588 } 589 590 return error; 591 } 592 593 594 static FT_Error 595 ft_stroke_border_moveto( FT_StrokeBorder border, 596 FT_Vector* to ) 597 { 598 /* close current open path if any ? */ 599 if ( border->start >= 0 ) 600 ft_stroke_border_close( border, FALSE ); 601 602 border->start = (FT_Int)border->num_points; 603 border->movable = FALSE; 604 605 return ft_stroke_border_lineto( border, to, FALSE ); 606 } 607 608 609 static void 610 ft_stroke_border_init( FT_StrokeBorder border, 611 FT_Memory memory ) 612 { 613 border->memory = memory; 614 border->points = NULL; 615 border->tags = NULL; 616 617 border->num_points = 0; 618 border->max_points = 0; 619 border->start = -1; 620 border->valid = FALSE; 621 } 622 623 624 static void 625 ft_stroke_border_reset( FT_StrokeBorder border ) 626 { 627 border->num_points = 0; 628 border->start = -1; 629 border->valid = FALSE; 630 } 631 632 633 static void 634 ft_stroke_border_done( FT_StrokeBorder border ) 635 { 636 FT_Memory memory = border->memory; 637 638 639 FT_FREE( border->points ); 640 FT_FREE( border->tags ); 641 642 border->num_points = 0; 643 border->max_points = 0; 644 border->start = -1; 645 border->valid = FALSE; 646 } 647 648 649 static FT_Error 650 ft_stroke_border_get_counts( FT_StrokeBorder border, 651 FT_UInt *anum_points, 652 FT_UInt *anum_contours ) 653 { 654 FT_Error error = FT_Err_Ok; 655 FT_UInt num_points = 0; 656 FT_UInt num_contours = 0; 657 658 FT_UInt count = border->num_points; 659 FT_Vector* point = border->points; 660 FT_Byte* tags = border->tags; 661 FT_Int in_contour = 0; 662 663 664 for ( ; count > 0; count--, num_points++, point++, tags++ ) 665 { 666 if ( tags[0] & FT_STROKE_TAG_BEGIN ) 667 { 668 if ( in_contour != 0 ) 669 goto Fail; 670 671 in_contour = 1; 672 } 673 else if ( in_contour == 0 ) 674 goto Fail; 675 676 if ( tags[0] & FT_STROKE_TAG_END ) 677 { 678 in_contour = 0; 679 num_contours++; 680 } 681 } 682 683 if ( in_contour != 0 ) 684 goto Fail; 685 686 border->valid = TRUE; 687 688 Exit: 689 *anum_points = num_points; 690 *anum_contours = num_contours; 691 return error; 692 693 Fail: 694 num_points = 0; 695 num_contours = 0; 696 goto Exit; 697 } 698 699 700 static void 701 ft_stroke_border_export( FT_StrokeBorder border, 702 FT_Outline* outline ) 703 { 704 /* copy point locations */ 705 FT_ARRAY_COPY( outline->points + outline->n_points, 706 border->points, 707 border->num_points ); 708 709 /* copy tags */ 710 { 711 FT_UInt count = border->num_points; 712 FT_Byte* read = border->tags; 713 FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points; 714 715 716 for ( ; count > 0; count--, read++, write++ ) 717 { 718 if ( *read & FT_STROKE_TAG_ON ) 719 *write = FT_CURVE_TAG_ON; 720 else if ( *read & FT_STROKE_TAG_CUBIC ) 721 *write = FT_CURVE_TAG_CUBIC; 722 else 723 *write = FT_CURVE_TAG_CONIC; 724 } 725 } 726 727 /* copy contours */ 728 { 729 FT_UInt count = border->num_points; 730 FT_Byte* tags = border->tags; 731 FT_Short* write = outline->contours + outline->n_contours; 732 FT_Short idx = (FT_Short)outline->n_points; 733 734 735 for ( ; count > 0; count--, tags++, idx++ ) 736 { 737 if ( *tags & FT_STROKE_TAG_END ) 738 { 739 *write++ = idx; 740 outline->n_contours++; 741 } 742 } 743 } 744 745 outline->n_points += (short)border->num_points; 746 747 FT_ASSERT( FT_Outline_Check( outline ) == 0 ); 748 } 749 750 751 /*************************************************************************/ 752 /*************************************************************************/ 753 /***** *****/ 754 /***** STROKER *****/ 755 /***** *****/ 756 /*************************************************************************/ 757 /*************************************************************************/ 758 759 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI ) 760 761 typedef struct FT_StrokerRec_ 762 { 763 FT_Angle angle_in; /* direction into curr join */ 764 FT_Angle angle_out; /* direction out of join */ 765 FT_Vector center; /* current position */ 766 FT_Fixed line_length; /* length of last lineto */ 767 FT_Bool first_point; /* is this the start? */ 768 FT_Bool subpath_open; /* is the subpath open? */ 769 FT_Angle subpath_angle; /* subpath start direction */ 770 FT_Vector subpath_start; /* subpath start position */ 771 FT_Fixed subpath_line_length; /* subpath start lineto len */ 772 FT_Bool handle_wide_strokes; /* use wide strokes logic? */ 773 774 FT_Stroker_LineCap line_cap; 775 FT_Stroker_LineJoin line_join; 776 FT_Stroker_LineJoin line_join_saved; 777 FT_Fixed miter_limit; 778 FT_Fixed radius; 779 780 FT_StrokeBorderRec borders[2]; 781 FT_Library library; 782 783 } FT_StrokerRec; 784 785 786 /* documentation is in ftstroke.h */ 787 788 FT_EXPORT_DEF( FT_Error ) 789 FT_Stroker_New( FT_Library library, 790 FT_Stroker *astroker ) 791 { 792 FT_Error error; /* assigned in FT_NEW */ 793 FT_Memory memory; 794 FT_Stroker stroker = NULL; 795 796 797 if ( !library ) 798 return FT_THROW( Invalid_Library_Handle ); 799 800 if ( !astroker ) 801 return FT_THROW( Invalid_Argument ); 802 803 memory = library->memory; 804 805 if ( !FT_NEW( stroker ) ) 806 { 807 stroker->library = library; 808 809 ft_stroke_border_init( &stroker->borders[0], memory ); 810 ft_stroke_border_init( &stroker->borders[1], memory ); 811 } 812 813 *astroker = stroker; 814 815 return error; 816 } 817 818 819 /* documentation is in ftstroke.h */ 820 821 FT_EXPORT_DEF( void ) 822 FT_Stroker_Set( FT_Stroker stroker, 823 FT_Fixed radius, 824 FT_Stroker_LineCap line_cap, 825 FT_Stroker_LineJoin line_join, 826 FT_Fixed miter_limit ) 827 { 828 if ( !stroker ) 829 return; 830 831 stroker->radius = radius; 832 stroker->line_cap = line_cap; 833 stroker->line_join = line_join; 834 stroker->miter_limit = miter_limit; 835 836 /* ensure miter limit has sensible value */ 837 if ( stroker->miter_limit < 0x10000L ) 838 stroker->miter_limit = 0x10000L; 839 840 /* save line join style: */ 841 /* line join style can be temporarily changed when stroking curves */ 842 stroker->line_join_saved = line_join; 843 844 FT_Stroker_Rewind( stroker ); 845 } 846 847 848 /* documentation is in ftstroke.h */ 849 850 FT_EXPORT_DEF( void ) 851 FT_Stroker_Rewind( FT_Stroker stroker ) 852 { 853 if ( stroker ) 854 { 855 ft_stroke_border_reset( &stroker->borders[0] ); 856 ft_stroke_border_reset( &stroker->borders[1] ); 857 } 858 } 859 860 861 /* documentation is in ftstroke.h */ 862 863 FT_EXPORT_DEF( void ) 864 FT_Stroker_Done( FT_Stroker stroker ) 865 { 866 if ( stroker ) 867 { 868 FT_Memory memory = stroker->library->memory; 869 870 871 ft_stroke_border_done( &stroker->borders[0] ); 872 ft_stroke_border_done( &stroker->borders[1] ); 873 874 stroker->library = NULL; 875 FT_FREE( stroker ); 876 } 877 } 878 879 880 /* create a circular arc at a corner or cap */ 881 static FT_Error 882 ft_stroker_arcto( FT_Stroker stroker, 883 FT_Int side ) 884 { 885 FT_Angle total, rotate; 886 FT_Fixed radius = stroker->radius; 887 FT_Error error = FT_Err_Ok; 888 FT_StrokeBorder border = stroker->borders + side; 889 890 891 rotate = FT_SIDE_TO_ROTATE( side ); 892 893 total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 894 if ( total == FT_ANGLE_PI ) 895 total = -rotate * 2; 896 897 error = ft_stroke_border_arcto( border, 898 &stroker->center, 899 radius, 900 stroker->angle_in + rotate, 901 total ); 902 border->movable = FALSE; 903 return error; 904 } 905 906 907 /* add a cap at the end of an opened path */ 908 static FT_Error 909 ft_stroker_cap( FT_Stroker stroker, 910 FT_Angle angle, 911 FT_Int side ) 912 { 913 FT_Error error = FT_Err_Ok; 914 915 916 if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND ) 917 { 918 /* add a round cap */ 919 stroker->angle_in = angle; 920 stroker->angle_out = angle + FT_ANGLE_PI; 921 922 error = ft_stroker_arcto( stroker, side ); 923 } 924 else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE ) 925 { 926 /* add a square cap */ 927 FT_Vector delta, delta2; 928 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 929 FT_Fixed radius = stroker->radius; 930 FT_StrokeBorder border = stroker->borders + side; 931 932 933 FT_Vector_From_Polar( &delta2, radius, angle + rotate ); 934 FT_Vector_From_Polar( &delta, radius, angle ); 935 936 delta.x += stroker->center.x + delta2.x; 937 delta.y += stroker->center.y + delta2.y; 938 939 error = ft_stroke_border_lineto( border, &delta, FALSE ); 940 if ( error ) 941 goto Exit; 942 943 FT_Vector_From_Polar( &delta2, radius, angle - rotate ); 944 FT_Vector_From_Polar( &delta, radius, angle ); 945 946 delta.x += delta2.x + stroker->center.x; 947 delta.y += delta2.y + stroker->center.y; 948 949 error = ft_stroke_border_lineto( border, &delta, FALSE ); 950 } 951 else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT ) 952 { 953 /* add a butt ending */ 954 FT_Vector delta; 955 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 956 FT_Fixed radius = stroker->radius; 957 FT_StrokeBorder border = stroker->borders + side; 958 959 960 FT_Vector_From_Polar( &delta, radius, angle + rotate ); 961 962 delta.x += stroker->center.x; 963 delta.y += stroker->center.y; 964 965 error = ft_stroke_border_lineto( border, &delta, FALSE ); 966 if ( error ) 967 goto Exit; 968 969 FT_Vector_From_Polar( &delta, radius, angle - rotate ); 970 971 delta.x += stroker->center.x; 972 delta.y += stroker->center.y; 973 974 error = ft_stroke_border_lineto( border, &delta, FALSE ); 975 } 976 977 Exit: 978 return error; 979 } 980 981 982 /* process an inside corner, i.e. compute intersection */ 983 static FT_Error 984 ft_stroker_inside( FT_Stroker stroker, 985 FT_Int side, 986 FT_Fixed line_length ) 987 { 988 FT_StrokeBorder border = stroker->borders + side; 989 FT_Angle phi, theta, rotate; 990 FT_Fixed length, thcos; 991 FT_Vector delta; 992 FT_Error error = FT_Err_Ok; 993 FT_Bool intersect; /* use intersection of lines? */ 994 995 996 rotate = FT_SIDE_TO_ROTATE( side ); 997 998 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2; 999 1000 /* Only intersect borders if between two lineto's and both */ 1001 /* lines are long enough (line_length is zero for curves). */ 1002 /* Also avoid U-turns of nearly 180 degree. */ 1003 if ( !border->movable || line_length == 0 || 1004 theta > 0x59C000 || theta < -0x59C000 ) 1005 intersect = FALSE; 1006 else 1007 { 1008 /* compute minimum required length of lines */ 1009 FT_Fixed min_length = ft_pos_abs( FT_MulFix( stroker->radius, 1010 FT_Tan( theta ) ) ); 1011 1012 1013 intersect = FT_BOOL( min_length && 1014 stroker->line_length >= min_length && 1015 line_length >= min_length ); 1016 } 1017 1018 if ( !intersect ) 1019 { 1020 FT_Vector_From_Polar( &delta, stroker->radius, 1021 stroker->angle_out + rotate ); 1022 delta.x += stroker->center.x; 1023 delta.y += stroker->center.y; 1024 1025 border->movable = FALSE; 1026 } 1027 else 1028 { 1029 /* compute median angle */ 1030 phi = stroker->angle_in + theta; 1031 1032 thcos = FT_Cos( theta ); 1033 1034 length = FT_DivFix( stroker->radius, thcos ); 1035 1036 FT_Vector_From_Polar( &delta, length, phi + rotate ); 1037 delta.x += stroker->center.x; 1038 delta.y += stroker->center.y; 1039 } 1040 1041 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1042 1043 return error; 1044 } 1045 1046 1047 /* process an outside corner, i.e. compute bevel/miter/round */ 1048 static FT_Error 1049 ft_stroker_outside( FT_Stroker stroker, 1050 FT_Int side, 1051 FT_Fixed line_length ) 1052 { 1053 FT_StrokeBorder border = stroker->borders + side; 1054 FT_Error error; 1055 FT_Angle rotate; 1056 1057 1058 if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND ) 1059 error = ft_stroker_arcto( stroker, side ); 1060 else 1061 { 1062 /* this is a mitered (pointed) or beveled (truncated) corner */ 1063 FT_Fixed sigma = 0, radius = stroker->radius; 1064 FT_Angle theta = 0, phi = 0; 1065 FT_Fixed thcos = 0; 1066 FT_Bool bevel, fixed_bevel; 1067 1068 1069 rotate = FT_SIDE_TO_ROTATE( side ); 1070 1071 bevel = 1072 FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_BEVEL ); 1073 1074 fixed_bevel = 1075 FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE ); 1076 1077 if ( !bevel ) 1078 { 1079 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1080 1081 if ( theta == FT_ANGLE_PI ) 1082 { 1083 theta = rotate; 1084 phi = stroker->angle_in; 1085 } 1086 else 1087 { 1088 theta /= 2; 1089 phi = stroker->angle_in + theta + rotate; 1090 } 1091 1092 thcos = FT_Cos( theta ); 1093 sigma = FT_MulFix( stroker->miter_limit, thcos ); 1094 1095 /* is miter limit exceeded? */ 1096 if ( sigma < 0x10000L ) 1097 { 1098 /* don't create variable bevels for very small deviations; */ 1099 /* FT_Sin(x) = 0 for x <= 57 */ 1100 if ( fixed_bevel || ft_pos_abs( theta ) > 57 ) 1101 bevel = TRUE; 1102 } 1103 } 1104 1105 if ( bevel ) /* this is a bevel (broken angle) */ 1106 { 1107 if ( fixed_bevel ) 1108 { 1109 /* the outer corners are simply joined together */ 1110 FT_Vector delta; 1111 1112 1113 /* add bevel */ 1114 FT_Vector_From_Polar( &delta, 1115 radius, 1116 stroker->angle_out + rotate ); 1117 delta.x += stroker->center.x; 1118 delta.y += stroker->center.y; 1119 1120 border->movable = FALSE; 1121 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1122 } 1123 else /* variable bevel */ 1124 { 1125 /* the miter is truncated */ 1126 FT_Vector middle, delta; 1127 FT_Fixed length; 1128 1129 1130 /* compute middle point */ 1131 FT_Vector_From_Polar( &middle, 1132 FT_MulFix( radius, stroker->miter_limit ), 1133 phi ); 1134 middle.x += stroker->center.x; 1135 middle.y += stroker->center.y; 1136 1137 /* compute first angle point */ 1138 length = FT_MulDiv( radius, 0x10000L - sigma, 1139 ft_pos_abs( FT_Sin( theta ) ) ); 1140 1141 FT_Vector_From_Polar( &delta, length, phi + rotate ); 1142 delta.x += middle.x; 1143 delta.y += middle.y; 1144 1145 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1146 if ( error ) 1147 goto Exit; 1148 1149 /* compute second angle point */ 1150 FT_Vector_From_Polar( &delta, length, phi - rotate ); 1151 delta.x += middle.x; 1152 delta.y += middle.y; 1153 1154 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1155 if ( error ) 1156 goto Exit; 1157 1158 /* finally, add an end point; only needed if not lineto */ 1159 /* (line_length is zero for curves) */ 1160 if ( line_length == 0 ) 1161 { 1162 FT_Vector_From_Polar( &delta, 1163 radius, 1164 stroker->angle_out + rotate ); 1165 1166 delta.x += stroker->center.x; 1167 delta.y += stroker->center.y; 1168 1169 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1170 } 1171 } 1172 } 1173 else /* this is a miter (intersection) */ 1174 { 1175 FT_Fixed length; 1176 FT_Vector delta; 1177 1178 1179 length = FT_DivFix( stroker->radius, thcos ); 1180 1181 FT_Vector_From_Polar( &delta, length, phi ); 1182 delta.x += stroker->center.x; 1183 delta.y += stroker->center.y; 1184 1185 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1186 if ( error ) 1187 goto Exit; 1188 1189 /* now add an end point; only needed if not lineto */ 1190 /* (line_length is zero for curves) */ 1191 if ( line_length == 0 ) 1192 { 1193 FT_Vector_From_Polar( &delta, 1194 stroker->radius, 1195 stroker->angle_out + rotate ); 1196 delta.x += stroker->center.x; 1197 delta.y += stroker->center.y; 1198 1199 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1200 } 1201 } 1202 } 1203 1204 Exit: 1205 return error; 1206 } 1207 1208 1209 static FT_Error 1210 ft_stroker_process_corner( FT_Stroker stroker, 1211 FT_Fixed line_length ) 1212 { 1213 FT_Error error = FT_Err_Ok; 1214 FT_Angle turn; 1215 FT_Int inside_side; 1216 1217 1218 turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1219 1220 /* no specific corner processing is required if the turn is 0 */ 1221 if ( turn == 0 ) 1222 goto Exit; 1223 1224 /* when we turn to the right, the inside side is 0 */ 1225 /* otherwise, the inside side is 1 */ 1226 inside_side = ( turn < 0 ); 1227 1228 /* process the inside side */ 1229 error = ft_stroker_inside( stroker, inside_side, line_length ); 1230 if ( error ) 1231 goto Exit; 1232 1233 /* process the outside side */ 1234 error = ft_stroker_outside( stroker, !inside_side, line_length ); 1235 1236 Exit: 1237 return error; 1238 } 1239 1240 1241 /* add two points to the left and right borders corresponding to the */ 1242 /* start of the subpath */ 1243 static FT_Error 1244 ft_stroker_subpath_start( FT_Stroker stroker, 1245 FT_Angle start_angle, 1246 FT_Fixed line_length ) 1247 { 1248 FT_Vector delta; 1249 FT_Vector point; 1250 FT_Error error; 1251 FT_StrokeBorder border; 1252 1253 1254 FT_Vector_From_Polar( &delta, stroker->radius, 1255 start_angle + FT_ANGLE_PI2 ); 1256 1257 point.x = stroker->center.x + delta.x; 1258 point.y = stroker->center.y + delta.y; 1259 1260 border = stroker->borders; 1261 error = ft_stroke_border_moveto( border, &point ); 1262 if ( error ) 1263 goto Exit; 1264 1265 point.x = stroker->center.x - delta.x; 1266 point.y = stroker->center.y - delta.y; 1267 1268 border++; 1269 error = ft_stroke_border_moveto( border, &point ); 1270 1271 /* save angle, position, and line length for last join */ 1272 /* (line_length is zero for curves) */ 1273 stroker->subpath_angle = start_angle; 1274 stroker->first_point = FALSE; 1275 stroker->subpath_line_length = line_length; 1276 1277 Exit: 1278 return error; 1279 } 1280 1281 1282 /* documentation is in ftstroke.h */ 1283 1284 FT_EXPORT_DEF( FT_Error ) 1285 FT_Stroker_LineTo( FT_Stroker stroker, 1286 FT_Vector* to ) 1287 { 1288 FT_Error error = FT_Err_Ok; 1289 FT_StrokeBorder border; 1290 FT_Vector delta; 1291 FT_Angle angle; 1292 FT_Int side; 1293 FT_Fixed line_length; 1294 1295 1296 if ( !stroker || !to ) 1297 return FT_THROW( Invalid_Argument ); 1298 1299 delta.x = to->x - stroker->center.x; 1300 delta.y = to->y - stroker->center.y; 1301 1302 /* a zero-length lineto is a no-op; avoid creating a spurious corner */ 1303 if ( delta.x == 0 && delta.y == 0 ) 1304 goto Exit; 1305 1306 /* compute length of line */ 1307 line_length = FT_Vector_Length( &delta ); 1308 1309 angle = FT_Atan2( delta.x, delta.y ); 1310 FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 ); 1311 1312 /* process corner if necessary */ 1313 if ( stroker->first_point ) 1314 { 1315 /* This is the first segment of a subpath. We need to */ 1316 /* add a point to each border at their respective starting */ 1317 /* point locations. */ 1318 error = ft_stroker_subpath_start( stroker, angle, line_length ); 1319 if ( error ) 1320 goto Exit; 1321 } 1322 else 1323 { 1324 /* process the current corner */ 1325 stroker->angle_out = angle; 1326 error = ft_stroker_process_corner( stroker, line_length ); 1327 if ( error ) 1328 goto Exit; 1329 } 1330 1331 /* now add a line segment to both the `inside' and `outside' paths */ 1332 for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) 1333 { 1334 FT_Vector point; 1335 1336 1337 point.x = to->x + delta.x; 1338 point.y = to->y + delta.y; 1339 1340 /* the ends of lineto borders are movable */ 1341 error = ft_stroke_border_lineto( border, &point, TRUE ); 1342 if ( error ) 1343 goto Exit; 1344 1345 delta.x = -delta.x; 1346 delta.y = -delta.y; 1347 } 1348 1349 stroker->angle_in = angle; 1350 stroker->center = *to; 1351 stroker->line_length = line_length; 1352 1353 Exit: 1354 return error; 1355 } 1356 1357 1358 /* documentation is in ftstroke.h */ 1359 1360 FT_EXPORT_DEF( FT_Error ) 1361 FT_Stroker_ConicTo( FT_Stroker stroker, 1362 FT_Vector* control, 1363 FT_Vector* to ) 1364 { 1365 FT_Error error = FT_Err_Ok; 1366 FT_Vector bez_stack[34]; 1367 FT_Vector* arc; 1368 FT_Vector* limit = bez_stack + 30; 1369 FT_Bool first_arc = TRUE; 1370 1371 1372 if ( !stroker || !control || !to ) 1373 { 1374 error = FT_THROW( Invalid_Argument ); 1375 goto Exit; 1376 } 1377 1378 /* if all control points are coincident, this is a no-op; */ 1379 /* avoid creating a spurious corner */ 1380 if ( FT_IS_SMALL( stroker->center.x - control->x ) && 1381 FT_IS_SMALL( stroker->center.y - control->y ) && 1382 FT_IS_SMALL( control->x - to->x ) && 1383 FT_IS_SMALL( control->y - to->y ) ) 1384 { 1385 stroker->center = *to; 1386 goto Exit; 1387 } 1388 1389 arc = bez_stack; 1390 arc[0] = *to; 1391 arc[1] = *control; 1392 arc[2] = stroker->center; 1393 1394 while ( arc >= bez_stack ) 1395 { 1396 FT_Angle angle_in, angle_out; 1397 1398 1399 /* initialize with current direction */ 1400 angle_in = angle_out = stroker->angle_in; 1401 1402 if ( arc < limit && 1403 !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) 1404 { 1405 if ( stroker->first_point ) 1406 stroker->angle_in = angle_in; 1407 1408 ft_conic_split( arc ); 1409 arc += 2; 1410 continue; 1411 } 1412 1413 if ( first_arc ) 1414 { 1415 first_arc = FALSE; 1416 1417 /* process corner if necessary */ 1418 if ( stroker->first_point ) 1419 error = ft_stroker_subpath_start( stroker, angle_in, 0 ); 1420 else 1421 { 1422 stroker->angle_out = angle_in; 1423 error = ft_stroker_process_corner( stroker, 0 ); 1424 } 1425 } 1426 else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > 1427 FT_SMALL_CONIC_THRESHOLD / 4 ) 1428 { 1429 /* if the deviation from one arc to the next is too great, */ 1430 /* add a round corner */ 1431 stroker->center = arc[2]; 1432 stroker->angle_out = angle_in; 1433 stroker->line_join = FT_STROKER_LINEJOIN_ROUND; 1434 1435 error = ft_stroker_process_corner( stroker, 0 ); 1436 1437 /* reinstate line join style */ 1438 stroker->line_join = stroker->line_join_saved; 1439 } 1440 1441 if ( error ) 1442 goto Exit; 1443 1444 /* the arc's angle is small enough; we can add it directly to each */ 1445 /* border */ 1446 { 1447 FT_Vector ctrl, end; 1448 FT_Angle theta, phi, rotate, alpha0 = 0; 1449 FT_Fixed length; 1450 FT_StrokeBorder border; 1451 FT_Int side; 1452 1453 1454 theta = FT_Angle_Diff( angle_in, angle_out ) / 2; 1455 phi = angle_in + theta; 1456 length = FT_DivFix( stroker->radius, FT_Cos( theta ) ); 1457 1458 /* compute direction of original arc */ 1459 if ( stroker->handle_wide_strokes ) 1460 alpha0 = FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y ); 1461 1462 for ( border = stroker->borders, side = 0; 1463 side <= 1; 1464 side++, border++ ) 1465 { 1466 rotate = FT_SIDE_TO_ROTATE( side ); 1467 1468 /* compute control point */ 1469 FT_Vector_From_Polar( &ctrl, length, phi + rotate ); 1470 ctrl.x += arc[1].x; 1471 ctrl.y += arc[1].y; 1472 1473 /* compute end point */ 1474 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1475 end.x += arc[0].x; 1476 end.y += arc[0].y; 1477 1478 if ( stroker->handle_wide_strokes ) 1479 { 1480 FT_Vector start; 1481 FT_Angle alpha1; 1482 1483 1484 /* determine whether the border radius is greater than the */ 1485 /* radius of curvature of the original arc */ 1486 start = border->points[border->num_points - 1]; 1487 1488 alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); 1489 1490 /* is the direction of the border arc opposite to */ 1491 /* that of the original arc? */ 1492 if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > 1493 FT_ANGLE_PI / 2 ) 1494 { 1495 FT_Angle beta, gamma; 1496 FT_Vector bvec, delta; 1497 FT_Fixed blen, sinA, sinB, alen; 1498 1499 1500 /* use the sine rule to find the intersection point */ 1501 beta = FT_Atan2( arc[2].x - start.x, arc[2].y - start.y ); 1502 gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); 1503 1504 bvec.x = end.x - start.x; 1505 bvec.y = end.y - start.y; 1506 1507 blen = FT_Vector_Length( &bvec ); 1508 1509 sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); 1510 sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); 1511 1512 alen = FT_MulDiv( blen, sinA, sinB ); 1513 1514 FT_Vector_From_Polar( &delta, alen, beta ); 1515 delta.x += start.x; 1516 delta.y += start.y; 1517 1518 /* circumnavigate the negative sector backwards */ 1519 border->movable = FALSE; 1520 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1521 if ( error ) 1522 goto Exit; 1523 error = ft_stroke_border_lineto( border, &end, FALSE ); 1524 if ( error ) 1525 goto Exit; 1526 error = ft_stroke_border_conicto( border, &ctrl, &start ); 1527 if ( error ) 1528 goto Exit; 1529 /* and then move to the endpoint */ 1530 error = ft_stroke_border_lineto( border, &end, FALSE ); 1531 if ( error ) 1532 goto Exit; 1533 1534 continue; 1535 } 1536 1537 /* else fall through */ 1538 } 1539 1540 /* simply add an arc */ 1541 error = ft_stroke_border_conicto( border, &ctrl, &end ); 1542 if ( error ) 1543 goto Exit; 1544 } 1545 } 1546 1547 arc -= 2; 1548 1549 stroker->angle_in = angle_out; 1550 } 1551 1552 stroker->center = *to; 1553 1554 Exit: 1555 return error; 1556 } 1557 1558 1559 /* documentation is in ftstroke.h */ 1560 1561 FT_EXPORT_DEF( FT_Error ) 1562 FT_Stroker_CubicTo( FT_Stroker stroker, 1563 FT_Vector* control1, 1564 FT_Vector* control2, 1565 FT_Vector* to ) 1566 { 1567 FT_Error error = FT_Err_Ok; 1568 FT_Vector bez_stack[37]; 1569 FT_Vector* arc; 1570 FT_Vector* limit = bez_stack + 32; 1571 FT_Bool first_arc = TRUE; 1572 1573 1574 if ( !stroker || !control1 || !control2 || !to ) 1575 { 1576 error = FT_THROW( Invalid_Argument ); 1577 goto Exit; 1578 } 1579 1580 /* if all control points are coincident, this is a no-op; */ 1581 /* avoid creating a spurious corner */ 1582 if ( FT_IS_SMALL( stroker->center.x - control1->x ) && 1583 FT_IS_SMALL( stroker->center.y - control1->y ) && 1584 FT_IS_SMALL( control1->x - control2->x ) && 1585 FT_IS_SMALL( control1->y - control2->y ) && 1586 FT_IS_SMALL( control2->x - to->x ) && 1587 FT_IS_SMALL( control2->y - to->y ) ) 1588 { 1589 stroker->center = *to; 1590 goto Exit; 1591 } 1592 1593 arc = bez_stack; 1594 arc[0] = *to; 1595 arc[1] = *control2; 1596 arc[2] = *control1; 1597 arc[3] = stroker->center; 1598 1599 while ( arc >= bez_stack ) 1600 { 1601 FT_Angle angle_in, angle_mid, angle_out; 1602 1603 1604 /* initialize with current direction */ 1605 angle_in = angle_out = angle_mid = stroker->angle_in; 1606 1607 if ( arc < limit && 1608 !ft_cubic_is_small_enough( arc, &angle_in, 1609 &angle_mid, &angle_out ) ) 1610 { 1611 if ( stroker->first_point ) 1612 stroker->angle_in = angle_in; 1613 1614 ft_cubic_split( arc ); 1615 arc += 3; 1616 continue; 1617 } 1618 1619 if ( first_arc ) 1620 { 1621 first_arc = FALSE; 1622 1623 /* process corner if necessary */ 1624 if ( stroker->first_point ) 1625 error = ft_stroker_subpath_start( stroker, angle_in, 0 ); 1626 else 1627 { 1628 stroker->angle_out = angle_in; 1629 error = ft_stroker_process_corner( stroker, 0 ); 1630 } 1631 } 1632 else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > 1633 FT_SMALL_CUBIC_THRESHOLD / 4 ) 1634 { 1635 /* if the deviation from one arc to the next is too great, */ 1636 /* add a round corner */ 1637 stroker->center = arc[3]; 1638 stroker->angle_out = angle_in; 1639 stroker->line_join = FT_STROKER_LINEJOIN_ROUND; 1640 1641 error = ft_stroker_process_corner( stroker, 0 ); 1642 1643 /* reinstate line join style */ 1644 stroker->line_join = stroker->line_join_saved; 1645 } 1646 1647 if ( error ) 1648 goto Exit; 1649 1650 /* the arc's angle is small enough; we can add it directly to each */ 1651 /* border */ 1652 { 1653 FT_Vector ctrl1, ctrl2, end; 1654 FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0; 1655 FT_Fixed length1, length2; 1656 FT_StrokeBorder border; 1657 FT_Int side; 1658 1659 1660 theta1 = FT_Angle_Diff( angle_in, angle_mid ) / 2; 1661 theta2 = FT_Angle_Diff( angle_mid, angle_out ) / 2; 1662 phi1 = ft_angle_mean( angle_in, angle_mid ); 1663 phi2 = ft_angle_mean( angle_mid, angle_out ); 1664 length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) ); 1665 length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) ); 1666 1667 /* compute direction of original arc */ 1668 if ( stroker->handle_wide_strokes ) 1669 alpha0 = FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y ); 1670 1671 for ( border = stroker->borders, side = 0; 1672 side <= 1; 1673 side++, border++ ) 1674 { 1675 rotate = FT_SIDE_TO_ROTATE( side ); 1676 1677 /* compute control points */ 1678 FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); 1679 ctrl1.x += arc[2].x; 1680 ctrl1.y += arc[2].y; 1681 1682 FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); 1683 ctrl2.x += arc[1].x; 1684 ctrl2.y += arc[1].y; 1685 1686 /* compute end point */ 1687 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1688 end.x += arc[0].x; 1689 end.y += arc[0].y; 1690 1691 if ( stroker->handle_wide_strokes ) 1692 { 1693 FT_Vector start; 1694 FT_Angle alpha1; 1695 1696 1697 /* determine whether the border radius is greater than the */ 1698 /* radius of curvature of the original arc */ 1699 start = border->points[border->num_points - 1]; 1700 1701 alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); 1702 1703 /* is the direction of the border arc opposite to */ 1704 /* that of the original arc? */ 1705 if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > 1706 FT_ANGLE_PI / 2 ) 1707 { 1708 FT_Angle beta, gamma; 1709 FT_Vector bvec, delta; 1710 FT_Fixed blen, sinA, sinB, alen; 1711 1712 1713 /* use the sine rule to find the intersection point */ 1714 beta = FT_Atan2( arc[3].x - start.x, arc[3].y - start.y ); 1715 gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); 1716 1717 bvec.x = end.x - start.x; 1718 bvec.y = end.y - start.y; 1719 1720 blen = FT_Vector_Length( &bvec ); 1721 1722 sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); 1723 sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); 1724 1725 alen = FT_MulDiv( blen, sinA, sinB ); 1726 1727 FT_Vector_From_Polar( &delta, alen, beta ); 1728 delta.x += start.x; 1729 delta.y += start.y; 1730 1731 /* circumnavigate the negative sector backwards */ 1732 border->movable = FALSE; 1733 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1734 if ( error ) 1735 goto Exit; 1736 error = ft_stroke_border_lineto( border, &end, FALSE ); 1737 if ( error ) 1738 goto Exit; 1739 error = ft_stroke_border_cubicto( border, 1740 &ctrl2, 1741 &ctrl1, 1742 &start ); 1743 if ( error ) 1744 goto Exit; 1745 /* and then move to the endpoint */ 1746 error = ft_stroke_border_lineto( border, &end, FALSE ); 1747 if ( error ) 1748 goto Exit; 1749 1750 continue; 1751 } 1752 1753 /* else fall through */ 1754 } 1755 1756 /* simply add an arc */ 1757 error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end ); 1758 if ( error ) 1759 goto Exit; 1760 } 1761 } 1762 1763 arc -= 3; 1764 1765 stroker->angle_in = angle_out; 1766 } 1767 1768 stroker->center = *to; 1769 1770 Exit: 1771 return error; 1772 } 1773 1774 1775 /* documentation is in ftstroke.h */ 1776 1777 FT_EXPORT_DEF( FT_Error ) 1778 FT_Stroker_BeginSubPath( FT_Stroker stroker, 1779 FT_Vector* to, 1780 FT_Bool open ) 1781 { 1782 if ( !stroker || !to ) 1783 return FT_THROW( Invalid_Argument ); 1784 1785 /* We cannot process the first point, because there is not enough */ 1786 /* information regarding its corner/cap. The latter will be processed */ 1787 /* in the `FT_Stroker_EndSubPath' routine. */ 1788 /* */ 1789 stroker->first_point = TRUE; 1790 stroker->center = *to; 1791 stroker->subpath_open = open; 1792 1793 /* Determine if we need to check whether the border radius is greater */ 1794 /* than the radius of curvature of a curve, to handle this case */ 1795 /* specially. This is only required if bevel joins or butt caps may */ 1796 /* be created, because round & miter joins and round & square caps */ 1797 /* cover the negative sector created with wide strokes. */ 1798 stroker->handle_wide_strokes = 1799 FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_ROUND || 1800 ( stroker->subpath_open && 1801 stroker->line_cap == FT_STROKER_LINECAP_BUTT ) ); 1802 1803 /* record the subpath start point for each border */ 1804 stroker->subpath_start = *to; 1805 1806 stroker->angle_in = 0; 1807 1808 return FT_Err_Ok; 1809 } 1810 1811 1812 static FT_Error 1813 ft_stroker_add_reverse_left( FT_Stroker stroker, 1814 FT_Bool open ) 1815 { 1816 FT_StrokeBorder right = stroker->borders + 0; 1817 FT_StrokeBorder left = stroker->borders + 1; 1818 FT_Int new_points; 1819 FT_Error error = FT_Err_Ok; 1820 1821 1822 FT_ASSERT( left->start >= 0 ); 1823 1824 new_points = (FT_Int)left->num_points - left->start; 1825 if ( new_points > 0 ) 1826 { 1827 error = ft_stroke_border_grow( right, (FT_UInt)new_points ); 1828 if ( error ) 1829 goto Exit; 1830 1831 { 1832 FT_Vector* dst_point = right->points + right->num_points; 1833 FT_Byte* dst_tag = right->tags + right->num_points; 1834 FT_Vector* src_point = left->points + left->num_points - 1; 1835 FT_Byte* src_tag = left->tags + left->num_points - 1; 1836 1837 1838 while ( src_point >= left->points + left->start ) 1839 { 1840 *dst_point = *src_point; 1841 *dst_tag = *src_tag; 1842 1843 if ( open ) 1844 dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END; 1845 else 1846 { 1847 FT_Byte ttag = 1848 (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END ); 1849 1850 1851 /* switch begin/end tags if necessary */ 1852 if ( ttag == FT_STROKE_TAG_BEGIN || 1853 ttag == FT_STROKE_TAG_END ) 1854 dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END; 1855 } 1856 1857 src_point--; 1858 src_tag--; 1859 dst_point++; 1860 dst_tag++; 1861 } 1862 } 1863 1864 left->num_points = (FT_UInt)left->start; 1865 right->num_points += (FT_UInt)new_points; 1866 1867 right->movable = FALSE; 1868 left->movable = FALSE; 1869 } 1870 1871 Exit: 1872 return error; 1873 } 1874 1875 1876 /* documentation is in ftstroke.h */ 1877 1878 /* there's a lot of magic in this function! */ 1879 FT_EXPORT_DEF( FT_Error ) 1880 FT_Stroker_EndSubPath( FT_Stroker stroker ) 1881 { 1882 FT_Error error = FT_Err_Ok; 1883 1884 1885 if ( !stroker ) 1886 { 1887 error = FT_THROW( Invalid_Argument ); 1888 goto Exit; 1889 } 1890 1891 if ( stroker->subpath_open ) 1892 { 1893 FT_StrokeBorder right = stroker->borders; 1894 1895 1896 /* All right, this is an opened path, we need to add a cap between */ 1897 /* right & left, add the reverse of left, then add a final cap */ 1898 /* between left & right. */ 1899 error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); 1900 if ( error ) 1901 goto Exit; 1902 1903 /* add reversed points from `left' to `right' */ 1904 error = ft_stroker_add_reverse_left( stroker, TRUE ); 1905 if ( error ) 1906 goto Exit; 1907 1908 /* now add the final cap */ 1909 stroker->center = stroker->subpath_start; 1910 error = ft_stroker_cap( stroker, 1911 stroker->subpath_angle + FT_ANGLE_PI, 0 ); 1912 if ( error ) 1913 goto Exit; 1914 1915 /* Now end the right subpath accordingly. The left one is */ 1916 /* rewind and doesn't need further processing. */ 1917 ft_stroke_border_close( right, FALSE ); 1918 } 1919 else 1920 { 1921 FT_Angle turn; 1922 FT_Int inside_side; 1923 1924 1925 /* close the path if needed */ 1926 if ( stroker->center.x != stroker->subpath_start.x || 1927 stroker->center.y != stroker->subpath_start.y ) 1928 { 1929 error = FT_Stroker_LineTo( stroker, &stroker->subpath_start ); 1930 if ( error ) 1931 goto Exit; 1932 } 1933 1934 /* process the corner */ 1935 stroker->angle_out = stroker->subpath_angle; 1936 turn = FT_Angle_Diff( stroker->angle_in, 1937 stroker->angle_out ); 1938 1939 /* no specific corner processing is required if the turn is 0 */ 1940 if ( turn != 0 ) 1941 { 1942 /* when we turn to the right, the inside side is 0 */ 1943 /* otherwise, the inside side is 1 */ 1944 inside_side = ( turn < 0 ); 1945 1946 error = ft_stroker_inside( stroker, 1947 inside_side, 1948 stroker->subpath_line_length ); 1949 if ( error ) 1950 goto Exit; 1951 1952 /* process the outside side */ 1953 error = ft_stroker_outside( stroker, 1954 !inside_side, 1955 stroker->subpath_line_length ); 1956 if ( error ) 1957 goto Exit; 1958 } 1959 1960 /* then end our two subpaths */ 1961 ft_stroke_border_close( stroker->borders + 0, FALSE ); 1962 ft_stroke_border_close( stroker->borders + 1, TRUE ); 1963 } 1964 1965 Exit: 1966 return error; 1967 } 1968 1969 1970 /* documentation is in ftstroke.h */ 1971 1972 FT_EXPORT_DEF( FT_Error ) 1973 FT_Stroker_GetBorderCounts( FT_Stroker stroker, 1974 FT_StrokerBorder border, 1975 FT_UInt *anum_points, 1976 FT_UInt *anum_contours ) 1977 { 1978 FT_UInt num_points = 0, num_contours = 0; 1979 FT_Error error; 1980 1981 1982 if ( !stroker || border > 1 ) 1983 { 1984 error = FT_THROW( Invalid_Argument ); 1985 goto Exit; 1986 } 1987 1988 error = ft_stroke_border_get_counts( stroker->borders + border, 1989 &num_points, &num_contours ); 1990 Exit: 1991 if ( anum_points ) 1992 *anum_points = num_points; 1993 1994 if ( anum_contours ) 1995 *anum_contours = num_contours; 1996 1997 return error; 1998 } 1999 2000 2001 /* documentation is in ftstroke.h */ 2002 2003 FT_EXPORT_DEF( FT_Error ) 2004 FT_Stroker_GetCounts( FT_Stroker stroker, 2005 FT_UInt *anum_points, 2006 FT_UInt *anum_contours ) 2007 { 2008 FT_UInt count1, count2, num_points = 0; 2009 FT_UInt count3, count4, num_contours = 0; 2010 FT_Error error; 2011 2012 2013 if ( !stroker ) 2014 { 2015 error = FT_THROW( Invalid_Argument ); 2016 goto Exit; 2017 } 2018 2019 error = ft_stroke_border_get_counts( stroker->borders + 0, 2020 &count1, &count2 ); 2021 if ( error ) 2022 goto Exit; 2023 2024 error = ft_stroke_border_get_counts( stroker->borders + 1, 2025 &count3, &count4 ); 2026 if ( error ) 2027 goto Exit; 2028 2029 num_points = count1 + count3; 2030 num_contours = count2 + count4; 2031 2032 Exit: 2033 if ( anum_points ) 2034 *anum_points = num_points; 2035 2036 if ( anum_contours ) 2037 *anum_contours = num_contours; 2038 2039 return error; 2040 } 2041 2042 2043 /* documentation is in ftstroke.h */ 2044 2045 FT_EXPORT_DEF( void ) 2046 FT_Stroker_ExportBorder( FT_Stroker stroker, 2047 FT_StrokerBorder border, 2048 FT_Outline* outline ) 2049 { 2050 if ( !stroker || !outline ) 2051 return; 2052 2053 if ( border == FT_STROKER_BORDER_LEFT || 2054 border == FT_STROKER_BORDER_RIGHT ) 2055 { 2056 FT_StrokeBorder sborder = & stroker->borders[border]; 2057 2058 2059 if ( sborder->valid ) 2060 ft_stroke_border_export( sborder, outline ); 2061 } 2062 } 2063 2064 2065 /* documentation is in ftstroke.h */ 2066 2067 FT_EXPORT_DEF( void ) 2068 FT_Stroker_Export( FT_Stroker stroker, 2069 FT_Outline* outline ) 2070 { 2071 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline ); 2072 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline ); 2073 } 2074 2075 2076 /* documentation is in ftstroke.h */ 2077 2078 /* 2079 * The following is very similar to FT_Outline_Decompose, except 2080 * that we do support opened paths, and do not scale the outline. 2081 */ 2082 FT_EXPORT_DEF( FT_Error ) 2083 FT_Stroker_ParseOutline( FT_Stroker stroker, 2084 FT_Outline* outline, 2085 FT_Bool opened ) 2086 { 2087 FT_Vector v_last; 2088 FT_Vector v_control; 2089 FT_Vector v_start; 2090 2091 FT_Vector* point; 2092 FT_Vector* limit; 2093 char* tags; 2094 2095 FT_Error error; 2096 2097 FT_Int n; /* index of contour in outline */ 2098 FT_UInt first; /* index of first point in contour */ 2099 FT_Int tag; /* current point's state */ 2100 2101 2102 if ( !outline ) 2103 return FT_THROW( Invalid_Outline ); 2104 2105 if ( !stroker ) 2106 return FT_THROW( Invalid_Argument ); 2107 2108 FT_Stroker_Rewind( stroker ); 2109 2110 first = 0; 2111 2112 for ( n = 0; n < outline->n_contours; n++ ) 2113 { 2114 FT_UInt last; /* index of last point in contour */ 2115 2116 2117 last = (FT_UInt)outline->contours[n]; 2118 limit = outline->points + last; 2119 2120 /* skip empty points; we don't stroke these */ 2121 if ( last <= first ) 2122 { 2123 first = last + 1; 2124 continue; 2125 } 2126 2127 v_start = outline->points[first]; 2128 v_last = outline->points[last]; 2129 2130 v_control = v_start; 2131 2132 point = outline->points + first; 2133 tags = outline->tags + first; 2134 tag = FT_CURVE_TAG( tags[0] ); 2135 2136 /* A contour cannot start with a cubic control point! */ 2137 if ( tag == FT_CURVE_TAG_CUBIC ) 2138 goto Invalid_Outline; 2139 2140 /* check first point to determine origin */ 2141 if ( tag == FT_CURVE_TAG_CONIC ) 2142 { 2143 /* First point is conic control. Yes, this happens. */ 2144 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 2145 { 2146 /* start at last point if it is on the curve */ 2147 v_start = v_last; 2148 limit--; 2149 } 2150 else 2151 { 2152 /* if both first and last points are conic, */ 2153 /* start at their middle */ 2154 v_start.x = ( v_start.x + v_last.x ) / 2; 2155 v_start.y = ( v_start.y + v_last.y ) / 2; 2156 } 2157 point--; 2158 tags--; 2159 } 2160 2161 error = FT_Stroker_BeginSubPath( stroker, &v_start, opened ); 2162 if ( error ) 2163 goto Exit; 2164 2165 while ( point < limit ) 2166 { 2167 point++; 2168 tags++; 2169 2170 tag = FT_CURVE_TAG( tags[0] ); 2171 switch ( tag ) 2172 { 2173 case FT_CURVE_TAG_ON: /* emit a single line_to */ 2174 { 2175 FT_Vector vec; 2176 2177 2178 vec.x = point->x; 2179 vec.y = point->y; 2180 2181 error = FT_Stroker_LineTo( stroker, &vec ); 2182 if ( error ) 2183 goto Exit; 2184 continue; 2185 } 2186 2187 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 2188 v_control.x = point->x; 2189 v_control.y = point->y; 2190 2191 Do_Conic: 2192 if ( point < limit ) 2193 { 2194 FT_Vector vec; 2195 FT_Vector v_middle; 2196 2197 2198 point++; 2199 tags++; 2200 tag = FT_CURVE_TAG( tags[0] ); 2201 2202 vec = point[0]; 2203 2204 if ( tag == FT_CURVE_TAG_ON ) 2205 { 2206 error = FT_Stroker_ConicTo( stroker, &v_control, &vec ); 2207 if ( error ) 2208 goto Exit; 2209 continue; 2210 } 2211 2212 if ( tag != FT_CURVE_TAG_CONIC ) 2213 goto Invalid_Outline; 2214 2215 v_middle.x = ( v_control.x + vec.x ) / 2; 2216 v_middle.y = ( v_control.y + vec.y ) / 2; 2217 2218 error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); 2219 if ( error ) 2220 goto Exit; 2221 2222 v_control = vec; 2223 goto Do_Conic; 2224 } 2225 2226 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start ); 2227 goto Close; 2228 2229 default: /* FT_CURVE_TAG_CUBIC */ 2230 { 2231 FT_Vector vec1, vec2; 2232 2233 2234 if ( point + 1 > limit || 2235 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 2236 goto Invalid_Outline; 2237 2238 point += 2; 2239 tags += 2; 2240 2241 vec1 = point[-2]; 2242 vec2 = point[-1]; 2243 2244 if ( point <= limit ) 2245 { 2246 FT_Vector vec; 2247 2248 2249 vec = point[0]; 2250 2251 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); 2252 if ( error ) 2253 goto Exit; 2254 continue; 2255 } 2256 2257 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); 2258 goto Close; 2259 } 2260 } 2261 } 2262 2263 Close: 2264 if ( error ) 2265 goto Exit; 2266 2267 /* don't try to end the path if no segments have been generated */ 2268 if ( !stroker->first_point ) 2269 { 2270 error = FT_Stroker_EndSubPath( stroker ); 2271 if ( error ) 2272 goto Exit; 2273 } 2274 2275 first = last + 1; 2276 } 2277 2278 return FT_Err_Ok; 2279 2280 Exit: 2281 return error; 2282 2283 Invalid_Outline: 2284 return FT_THROW( Invalid_Outline ); 2285 } 2286 2287 2288 /* declare an extern to access `ft_outline_glyph_class' globally */ 2289 /* allocated in `ftglyph.c', and use the FT_OUTLINE_GLYPH_CLASS_GET */ 2290 /* macro to access it when FT_CONFIG_OPTION_PIC is defined */ 2291 #ifndef FT_CONFIG_OPTION_PIC 2292 extern const FT_Glyph_Class ft_outline_glyph_class; 2293 #endif 2294 #include "basepic.h" 2295 2296 2297 /* documentation is in ftstroke.h */ 2298 2299 FT_EXPORT_DEF( FT_Error ) 2300 FT_Glyph_Stroke( FT_Glyph *pglyph, 2301 FT_Stroker stroker, 2302 FT_Bool destroy ) 2303 { 2304 FT_Error error = FT_ERR( Invalid_Argument ); 2305 FT_Glyph glyph = NULL; 2306 2307 /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */ 2308 FT_Library library = stroker->library; 2309 2310 FT_UNUSED( library ); 2311 2312 2313 if ( !pglyph ) 2314 goto Exit; 2315 2316 glyph = *pglyph; 2317 if ( !glyph || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) 2318 goto Exit; 2319 2320 { 2321 FT_Glyph copy; 2322 2323 2324 error = FT_Glyph_Copy( glyph, © ); 2325 if ( error ) 2326 goto Exit; 2327 2328 glyph = copy; 2329 } 2330 2331 { 2332 FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; 2333 FT_Outline* outline = &oglyph->outline; 2334 FT_UInt num_points, num_contours; 2335 2336 2337 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 2338 if ( error ) 2339 goto Fail; 2340 2341 FT_Stroker_GetCounts( stroker, &num_points, &num_contours ); 2342 2343 FT_Outline_Done( glyph->library, outline ); 2344 2345 error = FT_Outline_New( glyph->library, 2346 num_points, 2347 (FT_Int)num_contours, 2348 outline ); 2349 if ( error ) 2350 goto Fail; 2351 2352 outline->n_points = 0; 2353 outline->n_contours = 0; 2354 2355 FT_Stroker_Export( stroker, outline ); 2356 } 2357 2358 if ( destroy ) 2359 FT_Done_Glyph( *pglyph ); 2360 2361 *pglyph = glyph; 2362 goto Exit; 2363 2364 Fail: 2365 FT_Done_Glyph( glyph ); 2366 glyph = NULL; 2367 2368 if ( !destroy ) 2369 *pglyph = NULL; 2370 2371 Exit: 2372 return error; 2373 } 2374 2375 2376 /* documentation is in ftstroke.h */ 2377 2378 FT_EXPORT_DEF( FT_Error ) 2379 FT_Glyph_StrokeBorder( FT_Glyph *pglyph, 2380 FT_Stroker stroker, 2381 FT_Bool inside, 2382 FT_Bool destroy ) 2383 { 2384 FT_Error error = FT_ERR( Invalid_Argument ); 2385 FT_Glyph glyph = NULL; 2386 2387 /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */ 2388 FT_Library library = stroker->library; 2389 2390 FT_UNUSED( library ); 2391 2392 2393 if ( !pglyph ) 2394 goto Exit; 2395 2396 glyph = *pglyph; 2397 if ( !glyph || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) 2398 goto Exit; 2399 2400 { 2401 FT_Glyph copy; 2402 2403 2404 error = FT_Glyph_Copy( glyph, © ); 2405 if ( error ) 2406 goto Exit; 2407 2408 glyph = copy; 2409 } 2410 2411 { 2412 FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; 2413 FT_StrokerBorder border; 2414 FT_Outline* outline = &oglyph->outline; 2415 FT_UInt num_points, num_contours; 2416 2417 2418 border = FT_Outline_GetOutsideBorder( outline ); 2419 if ( inside ) 2420 { 2421 if ( border == FT_STROKER_BORDER_LEFT ) 2422 border = FT_STROKER_BORDER_RIGHT; 2423 else 2424 border = FT_STROKER_BORDER_LEFT; 2425 } 2426 2427 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 2428 if ( error ) 2429 goto Fail; 2430 2431 FT_Stroker_GetBorderCounts( stroker, border, 2432 &num_points, &num_contours ); 2433 2434 FT_Outline_Done( glyph->library, outline ); 2435 2436 error = FT_Outline_New( glyph->library, 2437 num_points, 2438 (FT_Int)num_contours, 2439 outline ); 2440 if ( error ) 2441 goto Fail; 2442 2443 outline->n_points = 0; 2444 outline->n_contours = 0; 2445 2446 FT_Stroker_ExportBorder( stroker, border, outline ); 2447 } 2448 2449 if ( destroy ) 2450 FT_Done_Glyph( *pglyph ); 2451 2452 *pglyph = glyph; 2453 goto Exit; 2454 2455 Fail: 2456 FT_Done_Glyph( glyph ); 2457 glyph = NULL; 2458 2459 if ( !destroy ) 2460 *pglyph = NULL; 2461 2462 Exit: 2463 return error; 2464 } 2465 2466 2467 /* END */ 2468