1 /***************************************************************************/ 2 /* */ 3 /* ftstroke.c */ 4 /* */ 5 /* FreeType path stroker (body). */ 6 /* */ 7 /* Copyright 2002-2006, 2008-2011 by */ 8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ 9 /* */ 10 /* This file is part of the FreeType project, and may only be used, */ 11 /* modified, and distributed under the terms of the FreeType project */ 12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 13 /* this file you indicate that you have read the license and */ 14 /* understand and accept it fully. */ 15 /* */ 16 /***************************************************************************/ 17 18 19 #include <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 = 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 = 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)( outline->n_points + 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; 793 FT_Memory memory; 794 FT_Stroker stroker = NULL; 795 796 797 if ( !library ) 798 return FT_Err_Invalid_Argument; 799 800 memory = library->memory; 801 802 if ( !FT_NEW( stroker ) ) 803 { 804 stroker->library = library; 805 806 ft_stroke_border_init( &stroker->borders[0], memory ); 807 ft_stroke_border_init( &stroker->borders[1], memory ); 808 } 809 810 *astroker = stroker; 811 812 return error; 813 } 814 815 816 /* documentation is in ftstroke.h */ 817 818 FT_EXPORT_DEF( void ) 819 FT_Stroker_Set( FT_Stroker stroker, 820 FT_Fixed radius, 821 FT_Stroker_LineCap line_cap, 822 FT_Stroker_LineJoin line_join, 823 FT_Fixed miter_limit ) 824 { 825 stroker->radius = radius; 826 stroker->line_cap = line_cap; 827 stroker->line_join = line_join; 828 stroker->miter_limit = miter_limit; 829 830 /* ensure miter limit has sensible value */ 831 if ( stroker->miter_limit < 0x10000 ) 832 stroker->miter_limit = 0x10000; 833 834 /* save line join style: */ 835 /* line join style can be temporarily changed when stroking curves */ 836 stroker->line_join_saved = line_join; 837 838 FT_Stroker_Rewind( stroker ); 839 } 840 841 842 /* documentation is in ftstroke.h */ 843 844 FT_EXPORT_DEF( void ) 845 FT_Stroker_Rewind( FT_Stroker stroker ) 846 { 847 if ( stroker ) 848 { 849 ft_stroke_border_reset( &stroker->borders[0] ); 850 ft_stroke_border_reset( &stroker->borders[1] ); 851 } 852 } 853 854 855 /* documentation is in ftstroke.h */ 856 857 FT_EXPORT_DEF( void ) 858 FT_Stroker_Done( FT_Stroker stroker ) 859 { 860 if ( stroker ) 861 { 862 FT_Memory memory = stroker->library->memory; 863 864 865 ft_stroke_border_done( &stroker->borders[0] ); 866 ft_stroke_border_done( &stroker->borders[1] ); 867 868 stroker->library = NULL; 869 FT_FREE( stroker ); 870 } 871 } 872 873 874 /* create a circular arc at a corner or cap */ 875 static FT_Error 876 ft_stroker_arcto( FT_Stroker stroker, 877 FT_Int side ) 878 { 879 FT_Angle total, rotate; 880 FT_Fixed radius = stroker->radius; 881 FT_Error error = FT_Err_Ok; 882 FT_StrokeBorder border = stroker->borders + side; 883 884 885 rotate = FT_SIDE_TO_ROTATE( side ); 886 887 total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 888 if ( total == FT_ANGLE_PI ) 889 total = -rotate * 2; 890 891 error = ft_stroke_border_arcto( border, 892 &stroker->center, 893 radius, 894 stroker->angle_in + rotate, 895 total ); 896 border->movable = FALSE; 897 return error; 898 } 899 900 901 /* add a cap at the end of an opened path */ 902 static FT_Error 903 ft_stroker_cap( FT_Stroker stroker, 904 FT_Angle angle, 905 FT_Int side ) 906 { 907 FT_Error error = FT_Err_Ok; 908 909 910 if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND ) 911 { 912 /* add a round cap */ 913 stroker->angle_in = angle; 914 stroker->angle_out = angle + FT_ANGLE_PI; 915 916 error = ft_stroker_arcto( stroker, side ); 917 } 918 else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE ) 919 { 920 /* add a square cap */ 921 FT_Vector delta, delta2; 922 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 923 FT_Fixed radius = stroker->radius; 924 FT_StrokeBorder border = stroker->borders + side; 925 926 927 FT_Vector_From_Polar( &delta2, radius, angle + rotate ); 928 FT_Vector_From_Polar( &delta, radius, angle ); 929 930 delta.x += stroker->center.x + delta2.x; 931 delta.y += stroker->center.y + delta2.y; 932 933 error = ft_stroke_border_lineto( border, &delta, FALSE ); 934 if ( error ) 935 goto Exit; 936 937 FT_Vector_From_Polar( &delta2, radius, angle - rotate ); 938 FT_Vector_From_Polar( &delta, radius, angle ); 939 940 delta.x += delta2.x + stroker->center.x; 941 delta.y += delta2.y + stroker->center.y; 942 943 error = ft_stroke_border_lineto( border, &delta, FALSE ); 944 } 945 else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT ) 946 { 947 /* add a butt ending */ 948 FT_Vector delta; 949 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 950 FT_Fixed radius = stroker->radius; 951 FT_StrokeBorder border = stroker->borders + side; 952 953 954 FT_Vector_From_Polar( &delta, radius, angle + rotate ); 955 956 delta.x += stroker->center.x; 957 delta.y += stroker->center.y; 958 959 error = ft_stroke_border_lineto( border, &delta, FALSE ); 960 if ( error ) 961 goto Exit; 962 963 FT_Vector_From_Polar( &delta, radius, angle - rotate ); 964 965 delta.x += stroker->center.x; 966 delta.y += stroker->center.y; 967 968 error = ft_stroke_border_lineto( border, &delta, FALSE ); 969 } 970 971 Exit: 972 return error; 973 } 974 975 976 /* process an inside corner, i.e. compute intersection */ 977 static FT_Error 978 ft_stroker_inside( FT_Stroker stroker, 979 FT_Int side, 980 FT_Fixed line_length ) 981 { 982 FT_StrokeBorder border = stroker->borders + side; 983 FT_Angle phi, theta, rotate; 984 FT_Fixed length, thcos; 985 FT_Vector delta; 986 FT_Error error = FT_Err_Ok; 987 FT_Bool intersect; /* use intersection of lines? */ 988 989 990 rotate = FT_SIDE_TO_ROTATE( side ); 991 992 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2; 993 994 /* Only intersect borders if between two lineto's and both */ 995 /* lines are long enough (line_length is zero for curves). */ 996 if ( !border->movable || line_length == 0 ) 997 intersect = FALSE; 998 else 999 { 1000 /* compute minimum required length of lines */ 1001 FT_Fixed min_length = ft_pos_abs( FT_MulFix( stroker->radius, 1002 FT_Tan( theta ) ) ); 1003 1004 1005 intersect = FT_BOOL( stroker->line_length >= min_length && 1006 line_length >= min_length ); 1007 } 1008 1009 if ( !intersect ) 1010 { 1011 FT_Vector_From_Polar( &delta, stroker->radius, 1012 stroker->angle_out + rotate ); 1013 delta.x += stroker->center.x; 1014 delta.y += stroker->center.y; 1015 1016 border->movable = FALSE; 1017 } 1018 else 1019 { 1020 /* compute median angle */ 1021 phi = stroker->angle_in + theta; 1022 1023 thcos = FT_Cos( theta ); 1024 1025 length = FT_DivFix( stroker->radius, thcos ); 1026 1027 FT_Vector_From_Polar( &delta, length, phi + rotate ); 1028 delta.x += stroker->center.x; 1029 delta.y += stroker->center.y; 1030 } 1031 1032 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1033 1034 return error; 1035 } 1036 1037 1038 /* process an outside corner, i.e. compute bevel/miter/round */ 1039 static FT_Error 1040 ft_stroker_outside( FT_Stroker stroker, 1041 FT_Int side, 1042 FT_Fixed line_length ) 1043 { 1044 FT_StrokeBorder border = stroker->borders + side; 1045 FT_Error error; 1046 FT_Angle rotate; 1047 1048 1049 if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND ) 1050 error = ft_stroker_arcto( stroker, side ); 1051 else 1052 { 1053 /* this is a mitered (pointed) or beveled (truncated) corner */ 1054 FT_Fixed sigma = 0, radius = stroker->radius; 1055 FT_Angle theta = 0, phi = 0; 1056 FT_Fixed thcos = 0; 1057 FT_Bool bevel, fixed_bevel; 1058 1059 1060 rotate = FT_SIDE_TO_ROTATE( side ); 1061 1062 bevel = 1063 FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_BEVEL ); 1064 1065 fixed_bevel = 1066 FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE ); 1067 1068 if ( !bevel ) 1069 { 1070 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1071 1072 if ( theta == FT_ANGLE_PI ) 1073 { 1074 theta = rotate; 1075 phi = stroker->angle_in; 1076 } 1077 else 1078 { 1079 theta /= 2; 1080 phi = stroker->angle_in + theta + rotate; 1081 } 1082 1083 thcos = FT_Cos( theta ); 1084 sigma = FT_MulFix( stroker->miter_limit, thcos ); 1085 1086 /* is miter limit exceeded? */ 1087 if ( sigma < 0x10000L ) 1088 { 1089 /* don't create variable bevels for very small deviations; */ 1090 /* FT_Sin(x) = 0 for x <= 57 */ 1091 if ( fixed_bevel || ft_pos_abs( theta ) > 57 ) 1092 bevel = TRUE; 1093 } 1094 } 1095 1096 if ( bevel ) /* this is a bevel (broken angle) */ 1097 { 1098 if ( fixed_bevel ) 1099 { 1100 /* the outer corners are simply joined together */ 1101 FT_Vector delta; 1102 1103 1104 /* add bevel */ 1105 FT_Vector_From_Polar( &delta, 1106 radius, 1107 stroker->angle_out + rotate ); 1108 delta.x += stroker->center.x; 1109 delta.y += stroker->center.y; 1110 1111 border->movable = FALSE; 1112 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1113 } 1114 else /* variable bevel */ 1115 { 1116 /* the miter is truncated */ 1117 FT_Vector middle, delta; 1118 FT_Fixed length; 1119 1120 1121 /* compute middle point */ 1122 FT_Vector_From_Polar( &middle, 1123 FT_MulFix( radius, stroker->miter_limit ), 1124 phi ); 1125 middle.x += stroker->center.x; 1126 middle.y += stroker->center.y; 1127 1128 /* compute first angle point */ 1129 length = FT_MulFix( radius, 1130 FT_DivFix( 0x10000L - sigma, 1131 ft_pos_abs( FT_Sin( theta ) ) ) ); 1132 1133 FT_Vector_From_Polar( &delta, length, phi + rotate ); 1134 delta.x += middle.x; 1135 delta.y += middle.y; 1136 1137 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1138 if ( error ) 1139 goto Exit; 1140 1141 /* compute second angle point */ 1142 FT_Vector_From_Polar( &delta, length, phi - rotate ); 1143 delta.x += middle.x; 1144 delta.y += middle.y; 1145 1146 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1147 if ( error ) 1148 goto Exit; 1149 1150 /* finally, add an end point; only needed if not lineto */ 1151 /* (line_length is zero for curves) */ 1152 if ( line_length == 0 ) 1153 { 1154 FT_Vector_From_Polar( &delta, 1155 radius, 1156 stroker->angle_out + rotate ); 1157 1158 delta.x += stroker->center.x; 1159 delta.y += stroker->center.y; 1160 1161 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1162 } 1163 } 1164 } 1165 else /* this is a miter (intersection) */ 1166 { 1167 FT_Fixed length; 1168 FT_Vector delta; 1169 1170 1171 length = FT_DivFix( stroker->radius, thcos ); 1172 1173 FT_Vector_From_Polar( &delta, length, phi ); 1174 delta.x += stroker->center.x; 1175 delta.y += stroker->center.y; 1176 1177 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1178 if ( error ) 1179 goto Exit; 1180 1181 /* now add an end point; only needed if not lineto */ 1182 /* (line_length is zero for curves) */ 1183 if ( line_length == 0 ) 1184 { 1185 FT_Vector_From_Polar( &delta, 1186 stroker->radius, 1187 stroker->angle_out + rotate ); 1188 delta.x += stroker->center.x; 1189 delta.y += stroker->center.y; 1190 1191 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1192 } 1193 } 1194 } 1195 1196 Exit: 1197 return error; 1198 } 1199 1200 1201 static FT_Error 1202 ft_stroker_process_corner( FT_Stroker stroker, 1203 FT_Fixed line_length ) 1204 { 1205 FT_Error error = FT_Err_Ok; 1206 FT_Angle turn; 1207 FT_Int inside_side; 1208 1209 1210 turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1211 1212 /* no specific corner processing is required if the turn is 0 */ 1213 if ( turn == 0 ) 1214 goto Exit; 1215 1216 /* when we turn to the right, the inside side is 0 */ 1217 inside_side = 0; 1218 1219 /* otherwise, the inside side is 1 */ 1220 if ( turn < 0 ) 1221 inside_side = 1; 1222 1223 /* process the inside side */ 1224 error = ft_stroker_inside( stroker, inside_side, line_length ); 1225 if ( error ) 1226 goto Exit; 1227 1228 /* process the outside side */ 1229 error = ft_stroker_outside( stroker, 1 - inside_side, line_length ); 1230 1231 Exit: 1232 return error; 1233 } 1234 1235 1236 /* add two points to the left and right borders corresponding to the */ 1237 /* start of the subpath */ 1238 static FT_Error 1239 ft_stroker_subpath_start( FT_Stroker stroker, 1240 FT_Angle start_angle, 1241 FT_Fixed line_length ) 1242 { 1243 FT_Vector delta; 1244 FT_Vector point; 1245 FT_Error error; 1246 FT_StrokeBorder border; 1247 1248 1249 FT_Vector_From_Polar( &delta, stroker->radius, 1250 start_angle + FT_ANGLE_PI2 ); 1251 1252 point.x = stroker->center.x + delta.x; 1253 point.y = stroker->center.y + delta.y; 1254 1255 border = stroker->borders; 1256 error = ft_stroke_border_moveto( border, &point ); 1257 if ( error ) 1258 goto Exit; 1259 1260 point.x = stroker->center.x - delta.x; 1261 point.y = stroker->center.y - delta.y; 1262 1263 border++; 1264 error = ft_stroke_border_moveto( border, &point ); 1265 1266 /* save angle, position, and line length for last join */ 1267 /* (line_length is zero for curves) */ 1268 stroker->subpath_angle = start_angle; 1269 stroker->first_point = FALSE; 1270 stroker->subpath_line_length = line_length; 1271 1272 Exit: 1273 return error; 1274 } 1275 1276 1277 /* documentation is in ftstroke.h */ 1278 1279 FT_EXPORT_DEF( FT_Error ) 1280 FT_Stroker_LineTo( FT_Stroker stroker, 1281 FT_Vector* to ) 1282 { 1283 FT_Error error = FT_Err_Ok; 1284 FT_StrokeBorder border; 1285 FT_Vector delta; 1286 FT_Angle angle; 1287 FT_Int side; 1288 FT_Fixed line_length; 1289 1290 1291 delta.x = to->x - stroker->center.x; 1292 delta.y = to->y - stroker->center.y; 1293 1294 /* a zero-length lineto is a no-op; avoid creating a spurious corner */ 1295 if ( delta.x == 0 && delta.y == 0 ) 1296 goto Exit; 1297 1298 /* compute length of line */ 1299 line_length = FT_Vector_Length( &delta ); 1300 1301 angle = FT_Atan2( delta.x, delta.y ); 1302 FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 ); 1303 1304 /* process corner if necessary */ 1305 if ( stroker->first_point ) 1306 { 1307 /* This is the first segment of a subpath. We need to */ 1308 /* add a point to each border at their respective starting */ 1309 /* point locations. */ 1310 error = ft_stroker_subpath_start( stroker, angle, line_length ); 1311 if ( error ) 1312 goto Exit; 1313 } 1314 else 1315 { 1316 /* process the current corner */ 1317 stroker->angle_out = angle; 1318 error = ft_stroker_process_corner( stroker, line_length ); 1319 if ( error ) 1320 goto Exit; 1321 } 1322 1323 /* now add a line segment to both the `inside' and `outside' paths */ 1324 for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) 1325 { 1326 FT_Vector point; 1327 1328 1329 point.x = to->x + delta.x; 1330 point.y = to->y + delta.y; 1331 1332 /* the ends of lineto borders are movable */ 1333 error = ft_stroke_border_lineto( border, &point, TRUE ); 1334 if ( error ) 1335 goto Exit; 1336 1337 delta.x = -delta.x; 1338 delta.y = -delta.y; 1339 } 1340 1341 stroker->angle_in = angle; 1342 stroker->center = *to; 1343 stroker->line_length = line_length; 1344 1345 Exit: 1346 return error; 1347 } 1348 1349 1350 /* documentation is in ftstroke.h */ 1351 1352 FT_EXPORT_DEF( FT_Error ) 1353 FT_Stroker_ConicTo( FT_Stroker stroker, 1354 FT_Vector* control, 1355 FT_Vector* to ) 1356 { 1357 FT_Error error = FT_Err_Ok; 1358 FT_Vector bez_stack[34]; 1359 FT_Vector* arc; 1360 FT_Vector* limit = bez_stack + 30; 1361 FT_Bool first_arc = TRUE; 1362 1363 1364 /* if all control points are coincident, this is a no-op; */ 1365 /* avoid creating a spurious corner */ 1366 if ( FT_IS_SMALL( stroker->center.x - control->x ) && 1367 FT_IS_SMALL( stroker->center.y - control->y ) && 1368 FT_IS_SMALL( control->x - to->x ) && 1369 FT_IS_SMALL( control->y - to->y ) ) 1370 { 1371 stroker->center = *to; 1372 goto Exit; 1373 } 1374 1375 arc = bez_stack; 1376 arc[0] = *to; 1377 arc[1] = *control; 1378 arc[2] = stroker->center; 1379 1380 while ( arc >= bez_stack ) 1381 { 1382 FT_Angle angle_in, angle_out; 1383 1384 1385 /* initialize with current direction */ 1386 angle_in = angle_out = stroker->angle_in; 1387 1388 if ( arc < limit && 1389 !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) 1390 { 1391 if ( stroker->first_point ) 1392 stroker->angle_in = angle_in; 1393 1394 ft_conic_split( arc ); 1395 arc += 2; 1396 continue; 1397 } 1398 1399 if ( first_arc ) 1400 { 1401 first_arc = FALSE; 1402 1403 /* process corner if necessary */ 1404 if ( stroker->first_point ) 1405 error = ft_stroker_subpath_start( stroker, angle_in, 0 ); 1406 else 1407 { 1408 stroker->angle_out = angle_in; 1409 error = ft_stroker_process_corner( stroker, 0 ); 1410 } 1411 } 1412 else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > 1413 FT_SMALL_CONIC_THRESHOLD / 4 ) 1414 { 1415 /* if the deviation from one arc to the next is too great, */ 1416 /* add a round corner */ 1417 stroker->center = arc[2]; 1418 stroker->angle_out = angle_in; 1419 stroker->line_join = FT_STROKER_LINEJOIN_ROUND; 1420 1421 error = ft_stroker_process_corner( stroker, 0 ); 1422 1423 /* reinstate line join style */ 1424 stroker->line_join = stroker->line_join_saved; 1425 } 1426 1427 if ( error ) 1428 goto Exit; 1429 1430 /* the arc's angle is small enough; we can add it directly to each */ 1431 /* border */ 1432 { 1433 FT_Vector ctrl, end; 1434 FT_Angle theta, phi, rotate, alpha0 = 0; 1435 FT_Fixed length; 1436 FT_StrokeBorder border; 1437 FT_Int side; 1438 1439 1440 theta = FT_Angle_Diff( angle_in, angle_out ) / 2; 1441 phi = angle_in + theta; 1442 length = FT_DivFix( stroker->radius, FT_Cos( theta ) ); 1443 1444 /* compute direction of original arc */ 1445 if ( stroker->handle_wide_strokes ) 1446 alpha0 = FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y ); 1447 1448 for ( border = stroker->borders, side = 0; 1449 side <= 1; 1450 side++, border++ ) 1451 { 1452 rotate = FT_SIDE_TO_ROTATE( side ); 1453 1454 /* compute control point */ 1455 FT_Vector_From_Polar( &ctrl, length, phi + rotate ); 1456 ctrl.x += arc[1].x; 1457 ctrl.y += arc[1].y; 1458 1459 /* compute end point */ 1460 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1461 end.x += arc[0].x; 1462 end.y += arc[0].y; 1463 1464 if ( stroker->handle_wide_strokes ) 1465 { 1466 FT_Vector start; 1467 FT_Angle alpha1; 1468 1469 1470 /* determine whether the border radius is greater than the */ 1471 /* radius of curvature of the original arc */ 1472 start = border->points[border->num_points - 1]; 1473 1474 alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); 1475 1476 /* is the direction of the border arc opposite to */ 1477 /* that of the original arc? */ 1478 if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > 1479 FT_ANGLE_PI / 2 ) 1480 { 1481 FT_Angle beta, gamma; 1482 FT_Vector bvec, delta; 1483 FT_Fixed blen, sinA, sinB, alen; 1484 1485 1486 /* use the sine rule to find the intersection point */ 1487 beta = FT_Atan2( arc[2].x - start.x, arc[2].y - start.y ); 1488 gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); 1489 1490 bvec.x = end.x - start.x; 1491 bvec.y = end.y - start.y; 1492 1493 blen = FT_Vector_Length( &bvec ); 1494 1495 sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); 1496 sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); 1497 1498 alen = FT_DivFix( FT_MulFix( blen, sinA ), sinB ); 1499 1500 FT_Vector_From_Polar( &delta, alen, beta ); 1501 delta.x += start.x; 1502 delta.y += start.y; 1503 1504 /* circumnavigate the negative sector backwards */ 1505 border->movable = FALSE; 1506 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1507 if ( error ) 1508 goto Exit; 1509 error = ft_stroke_border_lineto( border, &end, FALSE ); 1510 if ( error ) 1511 goto Exit; 1512 error = ft_stroke_border_conicto( border, &ctrl, &start ); 1513 if ( error ) 1514 goto Exit; 1515 /* and then move to the endpoint */ 1516 error = ft_stroke_border_lineto( border, &end, FALSE ); 1517 if ( error ) 1518 goto Exit; 1519 1520 continue; 1521 } 1522 1523 /* else fall through */ 1524 } 1525 1526 /* simply add an arc */ 1527 error = ft_stroke_border_conicto( border, &ctrl, &end ); 1528 if ( error ) 1529 goto Exit; 1530 } 1531 } 1532 1533 arc -= 2; 1534 1535 stroker->angle_in = angle_out; 1536 } 1537 1538 stroker->center = *to; 1539 1540 Exit: 1541 return error; 1542 } 1543 1544 1545 /* documentation is in ftstroke.h */ 1546 1547 FT_EXPORT_DEF( FT_Error ) 1548 FT_Stroker_CubicTo( FT_Stroker stroker, 1549 FT_Vector* control1, 1550 FT_Vector* control2, 1551 FT_Vector* to ) 1552 { 1553 FT_Error error = FT_Err_Ok; 1554 FT_Vector bez_stack[37]; 1555 FT_Vector* arc; 1556 FT_Vector* limit = bez_stack + 32; 1557 FT_Bool first_arc = TRUE; 1558 1559 1560 /* if all control points are coincident, this is a no-op; */ 1561 /* avoid creating a spurious corner */ 1562 if ( FT_IS_SMALL( stroker->center.x - control1->x ) && 1563 FT_IS_SMALL( stroker->center.y - control1->y ) && 1564 FT_IS_SMALL( control1->x - control2->x ) && 1565 FT_IS_SMALL( control1->y - control2->y ) && 1566 FT_IS_SMALL( control2->x - to->x ) && 1567 FT_IS_SMALL( control2->y - to->y ) ) 1568 { 1569 stroker->center = *to; 1570 goto Exit; 1571 } 1572 1573 arc = bez_stack; 1574 arc[0] = *to; 1575 arc[1] = *control2; 1576 arc[2] = *control1; 1577 arc[3] = stroker->center; 1578 1579 while ( arc >= bez_stack ) 1580 { 1581 FT_Angle angle_in, angle_mid, angle_out; 1582 1583 1584 /* initialize with current direction */ 1585 angle_in = angle_out = angle_mid = stroker->angle_in; 1586 1587 if ( arc < limit && 1588 !ft_cubic_is_small_enough( arc, &angle_in, 1589 &angle_mid, &angle_out ) ) 1590 { 1591 if ( stroker->first_point ) 1592 stroker->angle_in = angle_in; 1593 1594 ft_cubic_split( arc ); 1595 arc += 3; 1596 continue; 1597 } 1598 1599 if ( first_arc ) 1600 { 1601 first_arc = FALSE; 1602 1603 /* process corner if necessary */ 1604 if ( stroker->first_point ) 1605 error = ft_stroker_subpath_start( stroker, angle_in, 0 ); 1606 else 1607 { 1608 stroker->angle_out = angle_in; 1609 error = ft_stroker_process_corner( stroker, 0 ); 1610 } 1611 } 1612 else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > 1613 FT_SMALL_CUBIC_THRESHOLD / 4 ) 1614 { 1615 /* if the deviation from one arc to the next is too great, */ 1616 /* add a round corner */ 1617 stroker->center = arc[3]; 1618 stroker->angle_out = angle_in; 1619 stroker->line_join = FT_STROKER_LINEJOIN_ROUND; 1620 1621 error = ft_stroker_process_corner( stroker, 0 ); 1622 1623 /* reinstate line join style */ 1624 stroker->line_join = stroker->line_join_saved; 1625 } 1626 1627 if ( error ) 1628 goto Exit; 1629 1630 /* the arc's angle is small enough; we can add it directly to each */ 1631 /* border */ 1632 { 1633 FT_Vector ctrl1, ctrl2, end; 1634 FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0; 1635 FT_Fixed length1, length2; 1636 FT_StrokeBorder border; 1637 FT_Int side; 1638 1639 1640 theta1 = FT_Angle_Diff( angle_in, angle_mid ) / 2; 1641 theta2 = FT_Angle_Diff( angle_mid, angle_out ) / 2; 1642 phi1 = ft_angle_mean( angle_in, angle_mid ); 1643 phi2 = ft_angle_mean( angle_mid, angle_out ); 1644 length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) ); 1645 length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) ); 1646 1647 /* compute direction of original arc */ 1648 if ( stroker->handle_wide_strokes ) 1649 alpha0 = FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y ); 1650 1651 for ( border = stroker->borders, side = 0; 1652 side <= 1; 1653 side++, border++ ) 1654 { 1655 rotate = FT_SIDE_TO_ROTATE( side ); 1656 1657 /* compute control points */ 1658 FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); 1659 ctrl1.x += arc[2].x; 1660 ctrl1.y += arc[2].y; 1661 1662 FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); 1663 ctrl2.x += arc[1].x; 1664 ctrl2.y += arc[1].y; 1665 1666 /* compute end point */ 1667 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1668 end.x += arc[0].x; 1669 end.y += arc[0].y; 1670 1671 if ( stroker->handle_wide_strokes ) 1672 { 1673 FT_Vector start; 1674 FT_Angle alpha1; 1675 1676 1677 /* determine whether the border radius is greater than the */ 1678 /* radius of curvature of the original arc */ 1679 start = border->points[border->num_points - 1]; 1680 1681 alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); 1682 1683 /* is the direction of the border arc opposite to */ 1684 /* that of the original arc? */ 1685 if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > 1686 FT_ANGLE_PI / 2 ) 1687 { 1688 FT_Angle beta, gamma; 1689 FT_Vector bvec, delta; 1690 FT_Fixed blen, sinA, sinB, alen; 1691 1692 1693 /* use the sine rule to find the intersection point */ 1694 beta = FT_Atan2( arc[3].x - start.x, arc[3].y - start.y ); 1695 gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); 1696 1697 bvec.x = end.x - start.x; 1698 bvec.y = end.y - start.y; 1699 1700 blen = FT_Vector_Length( &bvec ); 1701 1702 sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); 1703 sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); 1704 1705 alen = FT_DivFix( FT_MulFix( blen, sinA ), sinB ); 1706 1707 FT_Vector_From_Polar( &delta, alen, beta ); 1708 delta.x += start.x; 1709 delta.y += start.y; 1710 1711 /* circumnavigate the negative sector backwards */ 1712 border->movable = FALSE; 1713 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1714 if ( error ) 1715 goto Exit; 1716 error = ft_stroke_border_lineto( border, &end, FALSE ); 1717 if ( error ) 1718 goto Exit; 1719 error = ft_stroke_border_cubicto( border, 1720 &ctrl2, 1721 &ctrl1, 1722 &start ); 1723 if ( error ) 1724 goto Exit; 1725 /* and then move to the endpoint */ 1726 error = ft_stroke_border_lineto( border, &end, FALSE ); 1727 if ( error ) 1728 goto Exit; 1729 1730 continue; 1731 } 1732 1733 /* else fall through */ 1734 } 1735 1736 /* simply add an arc */ 1737 error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end ); 1738 if ( error ) 1739 goto Exit; 1740 } 1741 } 1742 1743 arc -= 3; 1744 1745 stroker->angle_in = angle_out; 1746 } 1747 1748 stroker->center = *to; 1749 1750 Exit: 1751 return error; 1752 } 1753 1754 1755 /* documentation is in ftstroke.h */ 1756 1757 FT_EXPORT_DEF( FT_Error ) 1758 FT_Stroker_BeginSubPath( FT_Stroker stroker, 1759 FT_Vector* to, 1760 FT_Bool open ) 1761 { 1762 /* We cannot process the first point, because there is not enough */ 1763 /* information regarding its corner/cap. The latter will be processed */ 1764 /* in the `FT_Stroker_EndSubPath' routine. */ 1765 /* */ 1766 stroker->first_point = TRUE; 1767 stroker->center = *to; 1768 stroker->subpath_open = open; 1769 1770 /* Determine if we need to check whether the border radius is greater */ 1771 /* than the radius of curvature of a curve, to handle this case */ 1772 /* specially. This is only required if bevel joins or butt caps may */ 1773 /* be created, because round & miter joins and round & square caps */ 1774 /* cover the negative sector created with wide strokes. */ 1775 stroker->handle_wide_strokes = 1776 FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_ROUND || 1777 ( stroker->subpath_open && 1778 stroker->line_cap == FT_STROKER_LINECAP_BUTT ) ); 1779 1780 /* record the subpath start point for each border */ 1781 stroker->subpath_start = *to; 1782 1783 stroker->angle_in = 0; 1784 1785 return FT_Err_Ok; 1786 } 1787 1788 1789 static FT_Error 1790 ft_stroker_add_reverse_left( FT_Stroker stroker, 1791 FT_Bool open ) 1792 { 1793 FT_StrokeBorder right = stroker->borders + 0; 1794 FT_StrokeBorder left = stroker->borders + 1; 1795 FT_Int new_points; 1796 FT_Error error = FT_Err_Ok; 1797 1798 1799 FT_ASSERT( left->start >= 0 ); 1800 1801 new_points = left->num_points - left->start; 1802 if ( new_points > 0 ) 1803 { 1804 error = ft_stroke_border_grow( right, (FT_UInt)new_points ); 1805 if ( error ) 1806 goto Exit; 1807 1808 { 1809 FT_Vector* dst_point = right->points + right->num_points; 1810 FT_Byte* dst_tag = right->tags + right->num_points; 1811 FT_Vector* src_point = left->points + left->num_points - 1; 1812 FT_Byte* src_tag = left->tags + left->num_points - 1; 1813 1814 1815 while ( src_point >= left->points + left->start ) 1816 { 1817 *dst_point = *src_point; 1818 *dst_tag = *src_tag; 1819 1820 if ( open ) 1821 dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END; 1822 else 1823 { 1824 FT_Byte ttag = 1825 (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END ); 1826 1827 1828 /* switch begin/end tags if necessary */ 1829 if ( ttag == FT_STROKE_TAG_BEGIN || 1830 ttag == FT_STROKE_TAG_END ) 1831 dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END; 1832 } 1833 1834 src_point--; 1835 src_tag--; 1836 dst_point++; 1837 dst_tag++; 1838 } 1839 } 1840 1841 left->num_points = left->start; 1842 right->num_points += new_points; 1843 1844 right->movable = FALSE; 1845 left->movable = FALSE; 1846 } 1847 1848 Exit: 1849 return error; 1850 } 1851 1852 1853 /* documentation is in ftstroke.h */ 1854 1855 /* there's a lot of magic in this function! */ 1856 FT_EXPORT_DEF( FT_Error ) 1857 FT_Stroker_EndSubPath( FT_Stroker stroker ) 1858 { 1859 FT_Error error = FT_Err_Ok; 1860 1861 1862 if ( stroker->subpath_open ) 1863 { 1864 FT_StrokeBorder right = stroker->borders; 1865 1866 1867 /* All right, this is an opened path, we need to add a cap between */ 1868 /* right & left, add the reverse of left, then add a final cap */ 1869 /* between left & right. */ 1870 error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); 1871 if ( error ) 1872 goto Exit; 1873 1874 /* add reversed points from `left' to `right' */ 1875 error = ft_stroker_add_reverse_left( stroker, TRUE ); 1876 if ( error ) 1877 goto Exit; 1878 1879 /* now add the final cap */ 1880 stroker->center = stroker->subpath_start; 1881 error = ft_stroker_cap( stroker, 1882 stroker->subpath_angle + FT_ANGLE_PI, 0 ); 1883 if ( error ) 1884 goto Exit; 1885 1886 /* Now end the right subpath accordingly. The left one is */ 1887 /* rewind and doesn't need further processing. */ 1888 ft_stroke_border_close( right, FALSE ); 1889 } 1890 else 1891 { 1892 FT_Angle turn; 1893 FT_Int inside_side; 1894 1895 1896 /* close the path if needed */ 1897 if ( stroker->center.x != stroker->subpath_start.x || 1898 stroker->center.y != stroker->subpath_start.y ) 1899 { 1900 error = FT_Stroker_LineTo( stroker, &stroker->subpath_start ); 1901 if ( error ) 1902 goto Exit; 1903 } 1904 1905 /* process the corner */ 1906 stroker->angle_out = stroker->subpath_angle; 1907 turn = FT_Angle_Diff( stroker->angle_in, 1908 stroker->angle_out ); 1909 1910 /* no specific corner processing is required if the turn is 0 */ 1911 if ( turn != 0 ) 1912 { 1913 /* when we turn to the right, the inside side is 0 */ 1914 inside_side = 0; 1915 1916 /* otherwise, the inside side is 1 */ 1917 if ( turn < 0 ) 1918 inside_side = 1; 1919 1920 error = ft_stroker_inside( stroker, 1921 inside_side, 1922 stroker->subpath_line_length ); 1923 if ( error ) 1924 goto Exit; 1925 1926 /* process the outside side */ 1927 error = ft_stroker_outside( stroker, 1928 1 - inside_side, 1929 stroker->subpath_line_length ); 1930 if ( error ) 1931 goto Exit; 1932 } 1933 1934 /* then end our two subpaths */ 1935 ft_stroke_border_close( stroker->borders + 0, FALSE ); 1936 ft_stroke_border_close( stroker->borders + 1, TRUE ); 1937 } 1938 1939 Exit: 1940 return error; 1941 } 1942 1943 1944 /* documentation is in ftstroke.h */ 1945 1946 FT_EXPORT_DEF( FT_Error ) 1947 FT_Stroker_GetBorderCounts( FT_Stroker stroker, 1948 FT_StrokerBorder border, 1949 FT_UInt *anum_points, 1950 FT_UInt *anum_contours ) 1951 { 1952 FT_UInt num_points = 0, num_contours = 0; 1953 FT_Error error; 1954 1955 1956 if ( !stroker || border > 1 ) 1957 { 1958 error = FT_Err_Invalid_Argument; 1959 goto Exit; 1960 } 1961 1962 error = ft_stroke_border_get_counts( stroker->borders + border, 1963 &num_points, &num_contours ); 1964 Exit: 1965 if ( anum_points ) 1966 *anum_points = num_points; 1967 1968 if ( anum_contours ) 1969 *anum_contours = num_contours; 1970 1971 return error; 1972 } 1973 1974 1975 /* documentation is in ftstroke.h */ 1976 1977 FT_EXPORT_DEF( FT_Error ) 1978 FT_Stroker_GetCounts( FT_Stroker stroker, 1979 FT_UInt *anum_points, 1980 FT_UInt *anum_contours ) 1981 { 1982 FT_UInt count1, count2, num_points = 0; 1983 FT_UInt count3, count4, num_contours = 0; 1984 FT_Error error; 1985 1986 1987 error = ft_stroke_border_get_counts( stroker->borders + 0, 1988 &count1, &count2 ); 1989 if ( error ) 1990 goto Exit; 1991 1992 error = ft_stroke_border_get_counts( stroker->borders + 1, 1993 &count3, &count4 ); 1994 if ( error ) 1995 goto Exit; 1996 1997 num_points = count1 + count3; 1998 num_contours = count2 + count4; 1999 2000 Exit: 2001 *anum_points = num_points; 2002 *anum_contours = num_contours; 2003 return error; 2004 } 2005 2006 2007 /* documentation is in ftstroke.h */ 2008 2009 FT_EXPORT_DEF( void ) 2010 FT_Stroker_ExportBorder( FT_Stroker stroker, 2011 FT_StrokerBorder border, 2012 FT_Outline* outline ) 2013 { 2014 if ( border == FT_STROKER_BORDER_LEFT || 2015 border == FT_STROKER_BORDER_RIGHT ) 2016 { 2017 FT_StrokeBorder sborder = & stroker->borders[border]; 2018 2019 2020 if ( sborder->valid ) 2021 ft_stroke_border_export( sborder, outline ); 2022 } 2023 } 2024 2025 2026 /* documentation is in ftstroke.h */ 2027 2028 FT_EXPORT_DEF( void ) 2029 FT_Stroker_Export( FT_Stroker stroker, 2030 FT_Outline* outline ) 2031 { 2032 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline ); 2033 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline ); 2034 } 2035 2036 2037 /* documentation is in ftstroke.h */ 2038 2039 /* 2040 * The following is very similar to FT_Outline_Decompose, except 2041 * that we do support opened paths, and do not scale the outline. 2042 */ 2043 FT_EXPORT_DEF( FT_Error ) 2044 FT_Stroker_ParseOutline( FT_Stroker stroker, 2045 FT_Outline* outline, 2046 FT_Bool opened ) 2047 { 2048 FT_Vector v_last; 2049 FT_Vector v_control; 2050 FT_Vector v_start; 2051 2052 FT_Vector* point; 2053 FT_Vector* limit; 2054 char* tags; 2055 2056 FT_Error error; 2057 2058 FT_Int n; /* index of contour in outline */ 2059 FT_UInt first; /* index of first point in contour */ 2060 FT_Int tag; /* current point's state */ 2061 2062 2063 if ( !outline || !stroker ) 2064 return FT_Err_Invalid_Argument; 2065 2066 FT_Stroker_Rewind( stroker ); 2067 2068 first = 0; 2069 2070 for ( n = 0; n < outline->n_contours; n++ ) 2071 { 2072 FT_UInt last; /* index of last point in contour */ 2073 2074 2075 last = outline->contours[n]; 2076 limit = outline->points + last; 2077 2078 /* skip empty points; we don't stroke these */ 2079 if ( last <= first ) 2080 { 2081 first = last + 1; 2082 continue; 2083 } 2084 2085 v_start = outline->points[first]; 2086 v_last = outline->points[last]; 2087 2088 v_control = v_start; 2089 2090 point = outline->points + first; 2091 tags = outline->tags + first; 2092 tag = FT_CURVE_TAG( tags[0] ); 2093 2094 /* A contour cannot start with a cubic control point! */ 2095 if ( tag == FT_CURVE_TAG_CUBIC ) 2096 goto Invalid_Outline; 2097 2098 /* check first point to determine origin */ 2099 if ( tag == FT_CURVE_TAG_CONIC ) 2100 { 2101 /* First point is conic control. Yes, this happens. */ 2102 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 2103 { 2104 /* start at last point if it is on the curve */ 2105 v_start = v_last; 2106 limit--; 2107 } 2108 else 2109 { 2110 /* if both first and last points are conic, */ 2111 /* start at their middle */ 2112 v_start.x = ( v_start.x + v_last.x ) / 2; 2113 v_start.y = ( v_start.y + v_last.y ) / 2; 2114 } 2115 point--; 2116 tags--; 2117 } 2118 2119 error = FT_Stroker_BeginSubPath( stroker, &v_start, opened ); 2120 if ( error ) 2121 goto Exit; 2122 2123 while ( point < limit ) 2124 { 2125 point++; 2126 tags++; 2127 2128 tag = FT_CURVE_TAG( tags[0] ); 2129 switch ( tag ) 2130 { 2131 case FT_CURVE_TAG_ON: /* emit a single line_to */ 2132 { 2133 FT_Vector vec; 2134 2135 2136 vec.x = point->x; 2137 vec.y = point->y; 2138 2139 error = FT_Stroker_LineTo( stroker, &vec ); 2140 if ( error ) 2141 goto Exit; 2142 continue; 2143 } 2144 2145 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 2146 v_control.x = point->x; 2147 v_control.y = point->y; 2148 2149 Do_Conic: 2150 if ( point < limit ) 2151 { 2152 FT_Vector vec; 2153 FT_Vector v_middle; 2154 2155 2156 point++; 2157 tags++; 2158 tag = FT_CURVE_TAG( tags[0] ); 2159 2160 vec = point[0]; 2161 2162 if ( tag == FT_CURVE_TAG_ON ) 2163 { 2164 error = FT_Stroker_ConicTo( stroker, &v_control, &vec ); 2165 if ( error ) 2166 goto Exit; 2167 continue; 2168 } 2169 2170 if ( tag != FT_CURVE_TAG_CONIC ) 2171 goto Invalid_Outline; 2172 2173 v_middle.x = ( v_control.x + vec.x ) / 2; 2174 v_middle.y = ( v_control.y + vec.y ) / 2; 2175 2176 error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); 2177 if ( error ) 2178 goto Exit; 2179 2180 v_control = vec; 2181 goto Do_Conic; 2182 } 2183 2184 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start ); 2185 goto Close; 2186 2187 default: /* FT_CURVE_TAG_CUBIC */ 2188 { 2189 FT_Vector vec1, vec2; 2190 2191 2192 if ( point + 1 > limit || 2193 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 2194 goto Invalid_Outline; 2195 2196 point += 2; 2197 tags += 2; 2198 2199 vec1 = point[-2]; 2200 vec2 = point[-1]; 2201 2202 if ( point <= limit ) 2203 { 2204 FT_Vector vec; 2205 2206 2207 vec = point[0]; 2208 2209 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); 2210 if ( error ) 2211 goto Exit; 2212 continue; 2213 } 2214 2215 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); 2216 goto Close; 2217 } 2218 } 2219 } 2220 2221 Close: 2222 if ( error ) 2223 goto Exit; 2224 2225 /* don't try to end the path if no segments have been generated */ 2226 if ( !stroker->first_point ) 2227 { 2228 error = FT_Stroker_EndSubPath( stroker ); 2229 if ( error ) 2230 goto Exit; 2231 } 2232 2233 first = last + 1; 2234 } 2235 2236 return FT_Err_Ok; 2237 2238 Exit: 2239 return error; 2240 2241 Invalid_Outline: 2242 return FT_Err_Invalid_Outline; 2243 } 2244 2245 2246 /* declare an extern to access `ft_outline_glyph_class' globally */ 2247 /* allocated in `ftglyph.c', and use the FT_OUTLINE_GLYPH_CLASS_GET */ 2248 /* macro to access it when FT_CONFIG_OPTION_PIC is defined */ 2249 #ifndef FT_CONFIG_OPTION_PIC 2250 extern const FT_Glyph_Class ft_outline_glyph_class; 2251 #endif 2252 #include "basepic.h" 2253 2254 2255 /* documentation is in ftstroke.h */ 2256 2257 FT_EXPORT_DEF( FT_Error ) 2258 FT_Glyph_Stroke( FT_Glyph *pglyph, 2259 FT_Stroker stroker, 2260 FT_Bool destroy ) 2261 { 2262 FT_Error error = FT_Err_Invalid_Argument; 2263 FT_Glyph glyph = NULL; 2264 FT_Library library = stroker->library; 2265 2266 FT_UNUSED( library ); 2267 2268 2269 if ( pglyph == NULL ) 2270 goto Exit; 2271 2272 glyph = *pglyph; 2273 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) 2274 goto Exit; 2275 2276 { 2277 FT_Glyph copy; 2278 2279 2280 error = FT_Glyph_Copy( glyph, © ); 2281 if ( error ) 2282 goto Exit; 2283 2284 glyph = copy; 2285 } 2286 2287 { 2288 FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; 2289 FT_Outline* outline = &oglyph->outline; 2290 FT_UInt num_points, num_contours; 2291 2292 2293 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 2294 if ( error ) 2295 goto Fail; 2296 2297 (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours ); 2298 2299 FT_Outline_Done( glyph->library, outline ); 2300 2301 error = FT_Outline_New( glyph->library, 2302 num_points, num_contours, outline ); 2303 if ( error ) 2304 goto Fail; 2305 2306 outline->n_points = 0; 2307 outline->n_contours = 0; 2308 2309 FT_Stroker_Export( stroker, outline ); 2310 } 2311 2312 if ( destroy ) 2313 FT_Done_Glyph( *pglyph ); 2314 2315 *pglyph = glyph; 2316 goto Exit; 2317 2318 Fail: 2319 FT_Done_Glyph( glyph ); 2320 glyph = NULL; 2321 2322 if ( !destroy ) 2323 *pglyph = NULL; 2324 2325 Exit: 2326 return error; 2327 } 2328 2329 2330 /* documentation is in ftstroke.h */ 2331 2332 FT_EXPORT_DEF( FT_Error ) 2333 FT_Glyph_StrokeBorder( FT_Glyph *pglyph, 2334 FT_Stroker stroker, 2335 FT_Bool inside, 2336 FT_Bool destroy ) 2337 { 2338 FT_Error error = FT_Err_Invalid_Argument; 2339 FT_Glyph glyph = NULL; 2340 FT_Library library = stroker->library; 2341 2342 FT_UNUSED( library ); 2343 2344 2345 if ( pglyph == NULL ) 2346 goto Exit; 2347 2348 glyph = *pglyph; 2349 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) 2350 goto Exit; 2351 2352 { 2353 FT_Glyph copy; 2354 2355 2356 error = FT_Glyph_Copy( glyph, © ); 2357 if ( error ) 2358 goto Exit; 2359 2360 glyph = copy; 2361 } 2362 2363 { 2364 FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; 2365 FT_StrokerBorder border; 2366 FT_Outline* outline = &oglyph->outline; 2367 FT_UInt num_points, num_contours; 2368 2369 2370 border = FT_Outline_GetOutsideBorder( outline ); 2371 if ( inside ) 2372 { 2373 if ( border == FT_STROKER_BORDER_LEFT ) 2374 border = FT_STROKER_BORDER_RIGHT; 2375 else 2376 border = FT_STROKER_BORDER_LEFT; 2377 } 2378 2379 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 2380 if ( error ) 2381 goto Fail; 2382 2383 (void)FT_Stroker_GetBorderCounts( stroker, border, 2384 &num_points, &num_contours ); 2385 2386 FT_Outline_Done( glyph->library, outline ); 2387 2388 error = FT_Outline_New( glyph->library, 2389 num_points, 2390 num_contours, 2391 outline ); 2392 if ( error ) 2393 goto Fail; 2394 2395 outline->n_points = 0; 2396 outline->n_contours = 0; 2397 2398 FT_Stroker_ExportBorder( stroker, border, outline ); 2399 } 2400 2401 if ( destroy ) 2402 FT_Done_Glyph( *pglyph ); 2403 2404 *pglyph = glyph; 2405 goto Exit; 2406 2407 Fail: 2408 FT_Done_Glyph( glyph ); 2409 glyph = NULL; 2410 2411 if ( !destroy ) 2412 *pglyph = NULL; 2413 2414 Exit: 2415 return error; 2416 } 2417 2418 2419 /* END */ 2420