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