ftstroke.c revision 049d6fea481044fcc000e7782e5bc7046fc70844
1/***************************************************************************/ 2/* */ 3/* ftstroke.c */ 4/* */ 5/* FreeType path stroker (body). */ 6/* */ 7/* Copyright 2002, 2003, 2004, 2005, 2006, 2008 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 / 6 ) 64#define FT_EPSILON 2 65 66#define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON ) 67 68 69 static FT_Pos 70 ft_pos_abs( FT_Pos x ) 71 { 72 return x >= 0 ? x : -x ; 73 } 74 75 76 static void 77 ft_conic_split( FT_Vector* base ) 78 { 79 FT_Pos a, b; 80 81 82 base[4].x = base[2].x; 83 b = base[1].x; 84 a = base[3].x = ( base[2].x + b ) / 2; 85 b = base[1].x = ( base[0].x + b ) / 2; 86 base[2].x = ( a + b ) / 2; 87 88 base[4].y = base[2].y; 89 b = base[1].y; 90 a = base[3].y = ( base[2].y + b ) / 2; 91 b = base[1].y = ( base[0].y + b ) / 2; 92 base[2].y = ( a + b ) / 2; 93 } 94 95 96 static FT_Bool 97 ft_conic_is_small_enough( FT_Vector* base, 98 FT_Angle *angle_in, 99 FT_Angle *angle_out ) 100 { 101 FT_Vector d1, d2; 102 FT_Angle theta; 103 FT_Int close1, close2; 104 105 106 d1.x = base[1].x - base[2].x; 107 d1.y = base[1].y - base[2].y; 108 d2.x = base[0].x - base[1].x; 109 d2.y = base[0].y - base[1].y; 110 111 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 112 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 113 114 if ( close1 ) 115 { 116 if ( close2 ) 117 *angle_in = *angle_out = 0; 118 else 119 *angle_in = *angle_out = FT_Atan2( d2.x, d2.y ); 120 } 121 else if ( close2 ) 122 { 123 *angle_in = *angle_out = FT_Atan2( d1.x, d1.y ); 124 } 125 else 126 { 127 *angle_in = FT_Atan2( d1.x, d1.y ); 128 *angle_out = FT_Atan2( d2.x, d2.y ); 129 } 130 131 theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) ); 132 133 return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD ); 134 } 135 136 137 static void 138 ft_cubic_split( FT_Vector* base ) 139 { 140 FT_Pos a, b, c, d; 141 142 143 base[6].x = base[3].x; 144 c = base[1].x; 145 d = base[2].x; 146 base[1].x = a = ( base[0].x + c ) / 2; 147 base[5].x = b = ( base[3].x + d ) / 2; 148 c = ( c + d ) / 2; 149 base[2].x = a = ( a + c ) / 2; 150 base[4].x = b = ( b + c ) / 2; 151 base[3].x = ( a + b ) / 2; 152 153 base[6].y = base[3].y; 154 c = base[1].y; 155 d = base[2].y; 156 base[1].y = a = ( base[0].y + c ) / 2; 157 base[5].y = b = ( base[3].y + d ) / 2; 158 c = ( c + d ) / 2; 159 base[2].y = a = ( a + c ) / 2; 160 base[4].y = b = ( b + c ) / 2; 161 base[3].y = ( a + b ) / 2; 162 } 163 164 165 static FT_Bool 166 ft_cubic_is_small_enough( FT_Vector* base, 167 FT_Angle *angle_in, 168 FT_Angle *angle_mid, 169 FT_Angle *angle_out ) 170 { 171 FT_Vector d1, d2, d3; 172 FT_Angle theta1, theta2; 173 FT_Int close1, close2, close3; 174 175 176 d1.x = base[2].x - base[3].x; 177 d1.y = base[2].y - base[3].y; 178 d2.x = base[1].x - base[2].x; 179 d2.y = base[1].y - base[2].y; 180 d3.x = base[0].x - base[1].x; 181 d3.y = base[0].y - base[1].y; 182 183 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 184 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 185 close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y ); 186 187 if ( close1 || close3 ) 188 { 189 if ( close2 ) 190 { 191 /* basically a point */ 192 *angle_in = *angle_out = *angle_mid = 0; 193 } 194 else if ( close1 ) 195 { 196 *angle_in = *angle_mid = FT_Atan2( d2.x, d2.y ); 197 *angle_out = FT_Atan2( d3.x, d3.y ); 198 } 199 else /* close2 */ 200 { 201 *angle_in = FT_Atan2( d1.x, d1.y ); 202 *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y ); 203 } 204 } 205 else if ( close2 ) 206 { 207 *angle_in = *angle_mid = FT_Atan2( d1.x, d1.y ); 208 *angle_out = FT_Atan2( d3.x, d3.y ); 209 } 210 else 211 { 212 *angle_in = FT_Atan2( d1.x, d1.y ); 213 *angle_mid = FT_Atan2( d2.x, d2.y ); 214 *angle_out = FT_Atan2( d3.x, d3.y ); 215 } 216 217 theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) ); 218 theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) ); 219 220 return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD && 221 theta2 < FT_SMALL_CUBIC_THRESHOLD ); 222 } 223 224 225 /***************************************************************************/ 226 /***************************************************************************/ 227 /***** *****/ 228 /***** STROKE BORDERS *****/ 229 /***** *****/ 230 /***************************************************************************/ 231 /***************************************************************************/ 232 233 typedef enum FT_StrokeTags_ 234 { 235 FT_STROKE_TAG_ON = 1, /* on-curve point */ 236 FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ 237 FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ 238 FT_STROKE_TAG_END = 8 /* sub-path end */ 239 240 } FT_StrokeTags; 241 242#define FT_STROKE_TAG_BEGIN_END (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END) 243 244 typedef struct FT_StrokeBorderRec_ 245 { 246 FT_UInt num_points; 247 FT_UInt max_points; 248 FT_Vector* points; 249 FT_Byte* tags; 250 FT_Bool movable; 251 FT_Int start; /* index of current sub-path start point */ 252 FT_Memory memory; 253 FT_Bool valid; 254 255 } FT_StrokeBorderRec, *FT_StrokeBorder; 256 257 258 static FT_Error 259 ft_stroke_border_grow( FT_StrokeBorder border, 260 FT_UInt new_points ) 261 { 262 FT_UInt old_max = border->max_points; 263 FT_UInt new_max = border->num_points + new_points; 264 FT_Error error = 0; 265 266 267 if ( new_max > old_max ) 268 { 269 FT_UInt cur_max = old_max; 270 FT_Memory memory = border->memory; 271 272 273 while ( cur_max < new_max ) 274 cur_max += ( cur_max >> 1 ) + 16; 275 276 if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) || 277 FT_RENEW_ARRAY( border->tags, old_max, cur_max ) ) 278 goto Exit; 279 280 border->max_points = cur_max; 281 } 282 Exit: 283 return error; 284 } 285 286 287 static void 288 ft_stroke_border_close( FT_StrokeBorder border, 289 FT_Bool reverse ) 290 { 291 FT_UInt start = border->start; 292 FT_UInt count = border->num_points; 293 294 295 FT_ASSERT( border->start >= 0 ); 296 297 /* don't record empty paths! */ 298 if ( count <= start + 1U ) 299 border->num_points = start; 300 else 301 { 302 /* copy the last point to the start of this sub-path, since */ 303 /* it contains the `adjusted' starting coordinates */ 304 border->num_points = --count; 305 border->points[start] = border->points[count]; 306 307 if ( reverse ) 308 { 309 /* reverse the points */ 310 { 311 FT_Vector* vec1 = border->points + start + 1; 312 FT_Vector* vec2 = border->points + count - 1; 313 314 315 for ( ; vec1 < vec2; vec1++, vec2-- ) 316 { 317 FT_Vector tmp; 318 319 320 tmp = *vec1; 321 *vec1 = *vec2; 322 *vec2 = tmp; 323 } 324 } 325 326 /* then the tags */ 327 { 328 FT_Byte* tag1 = border->tags + start + 1; 329 FT_Byte* tag2 = border->tags + count - 1; 330 331 332 for ( ; tag1 < tag2; tag1++, tag2-- ) 333 { 334 FT_Byte tmp; 335 336 337 tmp = *tag1; 338 *tag1 = *tag2; 339 *tag2 = tmp; 340 } 341 } 342 } 343 344 border->tags[start ] |= FT_STROKE_TAG_BEGIN; 345 border->tags[count - 1] |= FT_STROKE_TAG_END; 346 } 347 348 border->start = -1; 349 border->movable = 0; 350 } 351 352 353 static FT_Error 354 ft_stroke_border_lineto( FT_StrokeBorder border, 355 FT_Vector* to, 356 FT_Bool movable ) 357 { 358 FT_Error error = 0; 359 360 361 FT_ASSERT( border->start >= 0 ); 362 363 if ( border->movable ) 364 { 365 /* move last point */ 366 border->points[border->num_points - 1] = *to; 367 } 368 else 369 { 370 /* add one point */ 371 error = ft_stroke_border_grow( border, 1 ); 372 if ( !error ) 373 { 374 FT_Vector* vec = border->points + border->num_points; 375 FT_Byte* tag = border->tags + border->num_points; 376 377 378 vec[0] = *to; 379 tag[0] = FT_STROKE_TAG_ON; 380 381 border->num_points += 1; 382 } 383 } 384 border->movable = movable; 385 return error; 386 } 387 388 389 static FT_Error 390 ft_stroke_border_conicto( FT_StrokeBorder border, 391 FT_Vector* control, 392 FT_Vector* to ) 393 { 394 FT_Error error; 395 396 397 FT_ASSERT( border->start >= 0 ); 398 399 error = ft_stroke_border_grow( border, 2 ); 400 if ( !error ) 401 { 402 FT_Vector* vec = border->points + border->num_points; 403 FT_Byte* tag = border->tags + border->num_points; 404 405 vec[0] = *control; 406 vec[1] = *to; 407 408 tag[0] = 0; 409 tag[1] = FT_STROKE_TAG_ON; 410 411 border->num_points += 2; 412 } 413 border->movable = 0; 414 return error; 415 } 416 417 418 static FT_Error 419 ft_stroke_border_cubicto( FT_StrokeBorder border, 420 FT_Vector* control1, 421 FT_Vector* control2, 422 FT_Vector* to ) 423 { 424 FT_Error error; 425 426 427 FT_ASSERT( border->start >= 0 ); 428 429 error = ft_stroke_border_grow( border, 3 ); 430 if ( !error ) 431 { 432 FT_Vector* vec = border->points + border->num_points; 433 FT_Byte* tag = border->tags + border->num_points; 434 435 436 vec[0] = *control1; 437 vec[1] = *control2; 438 vec[2] = *to; 439 440 tag[0] = FT_STROKE_TAG_CUBIC; 441 tag[1] = FT_STROKE_TAG_CUBIC; 442 tag[2] = FT_STROKE_TAG_ON; 443 444 border->num_points += 3; 445 } 446 border->movable = 0; 447 return error; 448 } 449 450 451#define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 ) 452 453 454 static FT_Error 455 ft_stroke_border_arcto( FT_StrokeBorder border, 456 FT_Vector* center, 457 FT_Fixed radius, 458 FT_Angle angle_start, 459 FT_Angle angle_diff ) 460 { 461 FT_Angle total, angle, step, rotate, next, theta; 462 FT_Vector a, b, a2, b2; 463 FT_Fixed length; 464 FT_Error error = 0; 465 466 467 /* compute start point */ 468 FT_Vector_From_Polar( &a, radius, angle_start ); 469 a.x += center->x; 470 a.y += center->y; 471 472 total = angle_diff; 473 angle = angle_start; 474 rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2; 475 476 while ( total != 0 ) 477 { 478 step = total; 479 if ( step > FT_ARC_CUBIC_ANGLE ) 480 step = FT_ARC_CUBIC_ANGLE; 481 482 else if ( step < -FT_ARC_CUBIC_ANGLE ) 483 step = -FT_ARC_CUBIC_ANGLE; 484 485 next = angle + step; 486 theta = step; 487 if ( theta < 0 ) 488 theta = -theta; 489 490 theta >>= 1; 491 492 /* compute end point */ 493 FT_Vector_From_Polar( &b, radius, next ); 494 b.x += center->x; 495 b.y += center->y; 496 497 /* compute first and second control points */ 498 length = FT_MulDiv( radius, FT_Sin( theta ) * 4, 499 ( 0x10000L + FT_Cos( theta ) ) * 3 ); 500 501 FT_Vector_From_Polar( &a2, length, angle + rotate ); 502 a2.x += a.x; 503 a2.y += a.y; 504 505 FT_Vector_From_Polar( &b2, length, next - rotate ); 506 b2.x += b.x; 507 b2.y += b.y; 508 509 /* add cubic arc */ 510 error = ft_stroke_border_cubicto( border, &a2, &b2, &b ); 511 if ( error ) 512 break; 513 514 /* process the rest of the arc ?? */ 515 a = b; 516 total -= step; 517 angle = next; 518 } 519 520 return error; 521 } 522 523 524 static FT_Error 525 ft_stroke_border_moveto( FT_StrokeBorder border, 526 FT_Vector* to ) 527 { 528 /* close current open path if any ? */ 529 if ( border->start >= 0 ) 530 ft_stroke_border_close( border, 0 ); 531 532 border->start = border->num_points; 533 border->movable = 0; 534 535 return ft_stroke_border_lineto( border, to, 0 ); 536 } 537 538 539 static void 540 ft_stroke_border_init( FT_StrokeBorder border, 541 FT_Memory memory ) 542 { 543 border->memory = memory; 544 border->points = NULL; 545 border->tags = NULL; 546 547 border->num_points = 0; 548 border->max_points = 0; 549 border->start = -1; 550 border->valid = 0; 551 } 552 553 554 static void 555 ft_stroke_border_reset( FT_StrokeBorder border ) 556 { 557 border->num_points = 0; 558 border->start = -1; 559 border->valid = 0; 560 } 561 562 563 static void 564 ft_stroke_border_done( FT_StrokeBorder border ) 565 { 566 FT_Memory memory = border->memory; 567 568 569 FT_FREE( border->points ); 570 FT_FREE( border->tags ); 571 572 border->num_points = 0; 573 border->max_points = 0; 574 border->start = -1; 575 border->valid = 0; 576 } 577 578 579 static FT_Error 580 ft_stroke_border_get_counts( FT_StrokeBorder border, 581 FT_UInt *anum_points, 582 FT_UInt *anum_contours ) 583 { 584 FT_Error error = 0; 585 FT_UInt num_points = 0; 586 FT_UInt num_contours = 0; 587 588 FT_UInt count = border->num_points; 589 FT_Vector* point = border->points; 590 FT_Byte* tags = border->tags; 591 FT_Int in_contour = 0; 592 593 594 for ( ; count > 0; count--, num_points++, point++, tags++ ) 595 { 596 if ( tags[0] & FT_STROKE_TAG_BEGIN ) 597 { 598 if ( in_contour != 0 ) 599 goto Fail; 600 601 in_contour = 1; 602 } 603 else if ( in_contour == 0 ) 604 goto Fail; 605 606 if ( tags[0] & FT_STROKE_TAG_END ) 607 { 608 if ( in_contour == 0 ) 609 goto Fail; 610 611 in_contour = 0; 612 num_contours++; 613 } 614 } 615 616 if ( in_contour != 0 ) 617 goto Fail; 618 619 border->valid = 1; 620 621 Exit: 622 *anum_points = num_points; 623 *anum_contours = num_contours; 624 return error; 625 626 Fail: 627 num_points = 0; 628 num_contours = 0; 629 goto Exit; 630 } 631 632 633 static void 634 ft_stroke_border_export( FT_StrokeBorder border, 635 FT_Outline* outline ) 636 { 637 /* copy point locations */ 638 FT_ARRAY_COPY( outline->points + outline->n_points, 639 border->points, 640 border->num_points ); 641 642 /* copy tags */ 643 { 644 FT_UInt count = border->num_points; 645 FT_Byte* read = border->tags; 646 FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points; 647 648 649 for ( ; count > 0; count--, read++, write++ ) 650 { 651 if ( *read & FT_STROKE_TAG_ON ) 652 *write = FT_CURVE_TAG_ON; 653 else if ( *read & FT_STROKE_TAG_CUBIC ) 654 *write = FT_CURVE_TAG_CUBIC; 655 else 656 *write = FT_CURVE_TAG_CONIC; 657 } 658 } 659 660 /* copy contours */ 661 { 662 FT_UInt count = border->num_points; 663 FT_Byte* tags = border->tags; 664 FT_Short* write = outline->contours + outline->n_contours; 665 FT_Short idx = (FT_Short)outline->n_points; 666 667 668 for ( ; count > 0; count--, tags++, idx++ ) 669 { 670 if ( *tags & FT_STROKE_TAG_END ) 671 { 672 *write++ = idx; 673 outline->n_contours++; 674 } 675 } 676 } 677 678 outline->n_points = (short)( outline->n_points + border->num_points ); 679 680 FT_ASSERT( FT_Outline_Check( outline ) == 0 ); 681 } 682 683 684 /***************************************************************************/ 685 /***************************************************************************/ 686 /***** *****/ 687 /***** STROKER *****/ 688 /***** *****/ 689 /***************************************************************************/ 690 /***************************************************************************/ 691 692#define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI ) 693 694 typedef struct FT_StrokerRec_ 695 { 696 FT_Angle angle_in; 697 FT_Angle angle_out; 698 FT_Vector center; 699 FT_Bool first_point; 700 FT_Bool subpath_open; 701 FT_Angle subpath_angle; 702 FT_Vector subpath_start; 703 704 FT_Stroker_LineCap line_cap; 705 FT_Stroker_LineJoin line_join; 706 FT_Fixed miter_limit; 707 FT_Fixed radius; 708 709 FT_Bool valid; 710 FT_StrokeBorderRec borders[2]; 711 FT_Memory memory; 712 713 } FT_StrokerRec; 714 715 716 /* documentation is in ftstroke.h */ 717 718 FT_EXPORT_DEF( FT_Error ) 719 FT_Stroker_New( FT_Library library, 720 FT_Stroker *astroker ) 721 { 722 FT_Error error; 723 FT_Memory memory; 724 FT_Stroker stroker; 725 726 727 if ( !library ) 728 return FT_Err_Invalid_Argument; 729 730 memory = library->memory; 731 732 if ( !FT_NEW( stroker ) ) 733 { 734 stroker->memory = memory; 735 736 ft_stroke_border_init( &stroker->borders[0], memory ); 737 ft_stroke_border_init( &stroker->borders[1], memory ); 738 } 739 *astroker = stroker; 740 return error; 741 } 742 743 744 /* documentation is in ftstroke.h */ 745 746 FT_EXPORT_DEF( void ) 747 FT_Stroker_Set( FT_Stroker stroker, 748 FT_Fixed radius, 749 FT_Stroker_LineCap line_cap, 750 FT_Stroker_LineJoin line_join, 751 FT_Fixed miter_limit ) 752 { 753 stroker->radius = radius; 754 stroker->line_cap = line_cap; 755 stroker->line_join = line_join; 756 stroker->miter_limit = miter_limit; 757 758 FT_Stroker_Rewind( stroker ); 759 } 760 761 762 /* documentation is in ftstroke.h */ 763 764 FT_EXPORT_DEF( void ) 765 FT_Stroker_Rewind( FT_Stroker stroker ) 766 { 767 if ( stroker ) 768 { 769 ft_stroke_border_reset( &stroker->borders[0] ); 770 ft_stroke_border_reset( &stroker->borders[1] ); 771 } 772 } 773 774 775 /* documentation is in ftstroke.h */ 776 777 FT_EXPORT_DEF( void ) 778 FT_Stroker_Done( FT_Stroker stroker ) 779 { 780 if ( stroker ) 781 { 782 FT_Memory memory = stroker->memory; 783 784 785 ft_stroke_border_done( &stroker->borders[0] ); 786 ft_stroke_border_done( &stroker->borders[1] ); 787 788 stroker->memory = NULL; 789 FT_FREE( stroker ); 790 } 791 } 792 793 794 /* creates a circular arc at a corner or cap */ 795 static FT_Error 796 ft_stroker_arcto( FT_Stroker stroker, 797 FT_Int side ) 798 { 799 FT_Angle total, rotate; 800 FT_Fixed radius = stroker->radius; 801 FT_Error error = 0; 802 FT_StrokeBorder border = stroker->borders + side; 803 804 805 rotate = FT_SIDE_TO_ROTATE( side ); 806 807 total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 808 if ( total == FT_ANGLE_PI ) 809 total = -rotate * 2; 810 811 error = ft_stroke_border_arcto( border, 812 &stroker->center, 813 radius, 814 stroker->angle_in + rotate, 815 total ); 816 border->movable = 0; 817 return error; 818 } 819 820 821 /* adds a cap at the end of an opened path */ 822 static FT_Error 823 ft_stroker_cap( FT_Stroker stroker, 824 FT_Angle angle, 825 FT_Int side ) 826 { 827 FT_Error error = 0; 828 829 830 if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND ) 831 { 832 /* add a round cap */ 833 stroker->angle_in = angle; 834 stroker->angle_out = angle + FT_ANGLE_PI; 835 error = ft_stroker_arcto( stroker, side ); 836 } 837 else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE ) 838 { 839 /* add a square cap */ 840 FT_Vector delta, delta2; 841 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 842 FT_Fixed radius = stroker->radius; 843 FT_StrokeBorder border = stroker->borders + side; 844 845 846 FT_Vector_From_Polar( &delta2, radius, angle + rotate ); 847 FT_Vector_From_Polar( &delta, radius, angle ); 848 849 delta.x += stroker->center.x + delta2.x; 850 delta.y += stroker->center.y + delta2.y; 851 852 error = ft_stroke_border_lineto( border, &delta, 0 ); 853 if ( error ) 854 goto Exit; 855 856 FT_Vector_From_Polar( &delta2, radius, angle - rotate ); 857 FT_Vector_From_Polar( &delta, radius, angle ); 858 859 delta.x += delta2.x + stroker->center.x; 860 delta.y += delta2.y + stroker->center.y; 861 862 error = ft_stroke_border_lineto( border, &delta, 0 ); 863 } 864 865 Exit: 866 return error; 867 } 868 869 870 /* process an inside corner, i.e. compute intersection */ 871 static FT_Error 872 ft_stroker_inside( FT_Stroker stroker, 873 FT_Int side) 874 { 875 FT_StrokeBorder border = stroker->borders + side; 876 FT_Angle phi, theta, rotate; 877 FT_Fixed length, thcos, sigma; 878 FT_Vector delta; 879 FT_Error error = 0; 880 881 882 rotate = FT_SIDE_TO_ROTATE( side ); 883 884 /* compute median angle */ 885 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 886 if ( theta == FT_ANGLE_PI ) 887 theta = rotate; 888 else 889 theta = theta / 2; 890 891 phi = stroker->angle_in + theta; 892 893 thcos = FT_Cos( theta ); 894 sigma = FT_MulFix( stroker->miter_limit, thcos ); 895 896 /* TODO: find better criterion to switch off the optimization */ 897 if ( sigma < 0x10000L ) 898 { 899 FT_Vector_From_Polar( &delta, stroker->radius, 900 stroker->angle_out + rotate ); 901 delta.x += stroker->center.x; 902 delta.y += stroker->center.y; 903 border->movable = 0; 904 } 905 else 906 { 907 length = FT_DivFix( stroker->radius, thcos ); 908 909 FT_Vector_From_Polar( &delta, length, phi + rotate ); 910 delta.x += stroker->center.x; 911 delta.y += stroker->center.y; 912 } 913 914 error = ft_stroke_border_lineto( border, &delta, 0 ); 915 916 return error; 917 } 918 919 920 /* process an outside corner, i.e. compute bevel/miter/round */ 921 static FT_Error 922 ft_stroker_outside( FT_Stroker stroker, 923 FT_Int side ) 924 { 925 FT_StrokeBorder border = stroker->borders + side; 926 FT_Error error; 927 FT_Angle rotate; 928 929 930 if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND ) 931 { 932 error = ft_stroker_arcto( stroker, side ); 933 } 934 else 935 { 936 /* this is a mitered or beveled corner */ 937 FT_Fixed sigma, radius = stroker->radius; 938 FT_Angle theta, phi; 939 FT_Fixed thcos; 940 FT_Bool miter; 941 942 943 rotate = FT_SIDE_TO_ROTATE( side ); 944 miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER ); 945 946 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 947 if ( theta == FT_ANGLE_PI ) 948 { 949 theta = rotate; 950 phi = stroker->angle_in; 951 } 952 else 953 { 954 theta = theta / 2; 955 phi = stroker->angle_in + theta + rotate; 956 } 957 958 thcos = FT_Cos( theta ); 959 sigma = FT_MulFix( stroker->miter_limit, thcos ); 960 961 if ( sigma >= 0x10000L ) 962 miter = 0; 963 964 if ( miter ) /* this is a miter (broken angle) */ 965 { 966 FT_Vector middle, delta; 967 FT_Fixed length; 968 969 970 /* compute middle point */ 971 FT_Vector_From_Polar( &middle, 972 FT_MulFix( radius, stroker->miter_limit ), 973 phi ); 974 middle.x += stroker->center.x; 975 middle.y += stroker->center.y; 976 977 /* compute first angle point */ 978 length = FT_MulFix( radius, 979 FT_DivFix( 0x10000L - sigma, 980 ft_pos_abs( FT_Sin( theta ) ) ) ); 981 982 FT_Vector_From_Polar( &delta, length, phi + rotate ); 983 delta.x += middle.x; 984 delta.y += middle.y; 985 986 error = ft_stroke_border_lineto( border, &delta, 0 ); 987 if ( error ) 988 goto Exit; 989 990 /* compute second angle point */ 991 FT_Vector_From_Polar( &delta, length, phi - rotate ); 992 delta.x += middle.x; 993 delta.y += middle.y; 994 995 error = ft_stroke_border_lineto( border, &delta, 0 ); 996 if ( error ) 997 goto Exit; 998 999 /* finally, add a movable end point */ 1000 FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate ); 1001 delta.x += stroker->center.x; 1002 delta.y += stroker->center.y; 1003 1004 error = ft_stroke_border_lineto( border, &delta, 1 ); 1005 } 1006 1007 else /* this is a bevel (intersection) */ 1008 { 1009 FT_Fixed length; 1010 FT_Vector delta; 1011 1012 1013 length = FT_DivFix( stroker->radius, thcos ); 1014 1015 FT_Vector_From_Polar( &delta, length, phi ); 1016 delta.x += stroker->center.x; 1017 delta.y += stroker->center.y; 1018 1019 error = ft_stroke_border_lineto( border, &delta, 0 ); 1020 if (error) goto Exit; 1021 1022 /* now add end point */ 1023 FT_Vector_From_Polar( &delta, stroker->radius, 1024 stroker->angle_out + rotate ); 1025 delta.x += stroker->center.x; 1026 delta.y += stroker->center.y; 1027 1028 error = ft_stroke_border_lineto( border, &delta, 1 ); 1029 } 1030 } 1031 1032 Exit: 1033 return error; 1034 } 1035 1036 1037 static FT_Error 1038 ft_stroker_process_corner( FT_Stroker stroker ) 1039 { 1040 FT_Error error = 0; 1041 FT_Angle turn; 1042 FT_Int inside_side; 1043 1044 1045 turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1046 1047 /* no specific corner processing is required if the turn is 0 */ 1048 if ( turn == 0 ) 1049 goto Exit; 1050 1051 /* when we turn to the right, the inside side is 0 */ 1052 inside_side = 0; 1053 1054 /* otherwise, the inside side is 1 */ 1055 if ( turn < 0 ) 1056 inside_side = 1; 1057 1058 /* process the inside side */ 1059 error = ft_stroker_inside( stroker, inside_side ); 1060 if ( error ) 1061 goto Exit; 1062 1063 /* process the outside side */ 1064 error = ft_stroker_outside( stroker, 1 - inside_side ); 1065 1066 Exit: 1067 return error; 1068 } 1069 1070 1071 /* add two points to the left and right borders corresponding to the */ 1072 /* start of the subpath.. */ 1073 static FT_Error 1074 ft_stroker_subpath_start( FT_Stroker stroker, 1075 FT_Angle start_angle ) 1076 { 1077 FT_Vector delta; 1078 FT_Vector point; 1079 FT_Error error; 1080 FT_StrokeBorder border; 1081 1082 1083 FT_Vector_From_Polar( &delta, stroker->radius, 1084 start_angle + FT_ANGLE_PI2 ); 1085 1086 point.x = stroker->center.x + delta.x; 1087 point.y = stroker->center.y + delta.y; 1088 1089 border = stroker->borders; 1090 error = ft_stroke_border_moveto( border, &point ); 1091 if ( error ) 1092 goto Exit; 1093 1094 point.x = stroker->center.x - delta.x; 1095 point.y = stroker->center.y - delta.y; 1096 1097 border++; 1098 error = ft_stroke_border_moveto( border, &point ); 1099 1100 /* save angle for last cap */ 1101 stroker->subpath_angle = start_angle; 1102 stroker->first_point = 0; 1103 1104 Exit: 1105 return error; 1106 } 1107 1108 1109 /* documentation is in ftstroke.h */ 1110 1111 FT_EXPORT_DEF( FT_Error ) 1112 FT_Stroker_LineTo( FT_Stroker stroker, 1113 FT_Vector* to ) 1114 { 1115 FT_Error error = 0; 1116 FT_StrokeBorder border; 1117 FT_Vector delta; 1118 FT_Angle angle; 1119 FT_Int side; 1120 1121 delta.x = to->x - stroker->center.x; 1122 delta.y = to->y - stroker->center.y; 1123 1124 angle = FT_Atan2( delta.x, delta.y ); 1125 FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 ); 1126 1127 /* process corner if necessary */ 1128 if ( stroker->first_point ) 1129 { 1130 /* This is the first segment of a subpath. We need to */ 1131 /* add a point to each border at their respective starting */ 1132 /* point locations. */ 1133 error = ft_stroker_subpath_start( stroker, angle ); 1134 if ( error ) 1135 goto Exit; 1136 } 1137 else 1138 { 1139 /* process the current corner */ 1140 stroker->angle_out = angle; 1141 error = ft_stroker_process_corner( stroker ); 1142 if ( error ) 1143 goto Exit; 1144 } 1145 1146 /* now add a line segment to both the "inside" and "outside" paths */ 1147 1148 for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) 1149 { 1150 FT_Vector point; 1151 1152 1153 point.x = to->x + delta.x; 1154 point.y = to->y + delta.y; 1155 1156 error = ft_stroke_border_lineto( border, &point, 1 ); 1157 if ( error ) 1158 goto Exit; 1159 1160 delta.x = -delta.x; 1161 delta.y = -delta.y; 1162 } 1163 1164 stroker->angle_in = angle; 1165 stroker->center = *to; 1166 1167 Exit: 1168 return error; 1169 } 1170 1171 1172 /* documentation is in ftstroke.h */ 1173 1174 FT_EXPORT_DEF( FT_Error ) 1175 FT_Stroker_ConicTo( FT_Stroker stroker, 1176 FT_Vector* control, 1177 FT_Vector* to ) 1178 { 1179 FT_Error error = 0; 1180 FT_Vector bez_stack[34]; 1181 FT_Vector* arc; 1182 FT_Vector* limit = bez_stack + 30; 1183 FT_Angle start_angle; 1184 FT_Bool first_arc = 1; 1185 1186 1187 arc = bez_stack; 1188 arc[0] = *to; 1189 arc[1] = *control; 1190 arc[2] = stroker->center; 1191 1192 while ( arc >= bez_stack ) 1193 { 1194 FT_Angle angle_in, angle_out; 1195 1196 1197 angle_in = angle_out = 0; /* remove compiler warnings */ 1198 1199 if ( arc < limit && 1200 !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) 1201 { 1202 ft_conic_split( arc ); 1203 arc += 2; 1204 continue; 1205 } 1206 1207 if ( first_arc ) 1208 { 1209 first_arc = 0; 1210 1211 start_angle = angle_in; 1212 1213 /* process corner if necessary */ 1214 if ( stroker->first_point ) 1215 error = ft_stroker_subpath_start( stroker, start_angle ); 1216 else 1217 { 1218 stroker->angle_out = start_angle; 1219 error = ft_stroker_process_corner( stroker ); 1220 } 1221 } 1222 1223 /* the arc's angle is small enough; we can add it directly to each */ 1224 /* border */ 1225 { 1226 FT_Vector ctrl, end; 1227 FT_Angle theta, phi, rotate; 1228 FT_Fixed length; 1229 FT_Int side; 1230 1231 1232 theta = FT_Angle_Diff( angle_in, angle_out ) / 2; 1233 phi = angle_in + theta; 1234 length = FT_DivFix( stroker->radius, FT_Cos( theta ) ); 1235 1236 for ( side = 0; side <= 1; side++ ) 1237 { 1238 rotate = FT_SIDE_TO_ROTATE( side ); 1239 1240 /* compute control point */ 1241 FT_Vector_From_Polar( &ctrl, length, phi + rotate ); 1242 ctrl.x += arc[1].x; 1243 ctrl.y += arc[1].y; 1244 1245 /* compute end point */ 1246 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1247 end.x += arc[0].x; 1248 end.y += arc[0].y; 1249 1250 error = ft_stroke_border_conicto( stroker->borders + side, 1251 &ctrl, &end ); 1252 if ( error ) 1253 goto Exit; 1254 } 1255 } 1256 1257 arc -= 2; 1258 1259 if ( arc < bez_stack ) 1260 stroker->angle_in = angle_out; 1261 } 1262 1263 stroker->center = *to; 1264 1265 Exit: 1266 return error; 1267 } 1268 1269 1270 /* documentation is in ftstroke.h */ 1271 1272 FT_EXPORT_DEF( FT_Error ) 1273 FT_Stroker_CubicTo( FT_Stroker stroker, 1274 FT_Vector* control1, 1275 FT_Vector* control2, 1276 FT_Vector* to ) 1277 { 1278 FT_Error error = 0; 1279 FT_Vector bez_stack[37]; 1280 FT_Vector* arc; 1281 FT_Vector* limit = bez_stack + 32; 1282 FT_Angle start_angle; 1283 FT_Bool first_arc = 1; 1284 1285 1286 arc = bez_stack; 1287 arc[0] = *to; 1288 arc[1] = *control2; 1289 arc[2] = *control1; 1290 arc[3] = stroker->center; 1291 1292 while ( arc >= bez_stack ) 1293 { 1294 FT_Angle angle_in, angle_mid, angle_out; 1295 1296 1297 /* remove compiler warnings */ 1298 angle_in = angle_out = angle_mid = 0; 1299 1300 if ( arc < limit && 1301 !ft_cubic_is_small_enough( arc, &angle_in, 1302 &angle_mid, &angle_out ) ) 1303 { 1304 ft_cubic_split( arc ); 1305 arc += 3; 1306 continue; 1307 } 1308 1309 if ( first_arc ) 1310 { 1311 first_arc = 0; 1312 1313 /* process corner if necessary */ 1314 start_angle = angle_in; 1315 1316 if ( stroker->first_point ) 1317 error = ft_stroker_subpath_start( stroker, start_angle ); 1318 else 1319 { 1320 stroker->angle_out = start_angle; 1321 error = ft_stroker_process_corner( stroker ); 1322 } 1323 if ( error ) 1324 goto Exit; 1325 } 1326 1327 /* the arc's angle is small enough; we can add it directly to each */ 1328 /* border */ 1329 { 1330 FT_Vector ctrl1, ctrl2, end; 1331 FT_Angle theta1, phi1, theta2, phi2, rotate; 1332 FT_Fixed length1, length2; 1333 FT_Int side; 1334 1335 1336 theta1 = ft_pos_abs( angle_mid - angle_in ) / 2; 1337 theta2 = ft_pos_abs( angle_out - angle_mid ) / 2; 1338 phi1 = (angle_mid + angle_in ) / 2; 1339 phi2 = (angle_mid + angle_out ) / 2; 1340 length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) ); 1341 length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) ); 1342 1343 for ( side = 0; side <= 1; side++ ) 1344 { 1345 rotate = FT_SIDE_TO_ROTATE( side ); 1346 1347 /* compute control points */ 1348 FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); 1349 ctrl1.x += arc[2].x; 1350 ctrl1.y += arc[2].y; 1351 1352 FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); 1353 ctrl2.x += arc[1].x; 1354 ctrl2.y += arc[1].y; 1355 1356 /* compute end point */ 1357 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1358 end.x += arc[0].x; 1359 end.y += arc[0].y; 1360 1361 error = ft_stroke_border_cubicto( stroker->borders + side, 1362 &ctrl1, &ctrl2, &end ); 1363 if ( error ) 1364 goto Exit; 1365 } 1366 } 1367 1368 arc -= 3; 1369 if ( arc < bez_stack ) 1370 stroker->angle_in = angle_out; 1371 } 1372 1373 stroker->center = *to; 1374 1375 Exit: 1376 return error; 1377 } 1378 1379 1380 /* documentation is in ftstroke.h */ 1381 1382 FT_EXPORT_DEF( FT_Error ) 1383 FT_Stroker_BeginSubPath( FT_Stroker stroker, 1384 FT_Vector* to, 1385 FT_Bool open ) 1386 { 1387 /* We cannot process the first point, because there is not enough */ 1388 /* information regarding its corner/cap. The latter will be processed */ 1389 /* in the "end_subpath" routine. */ 1390 /* */ 1391 stroker->first_point = 1; 1392 stroker->center = *to; 1393 stroker->subpath_open = open; 1394 1395 /* record the subpath start point index for each border */ 1396 stroker->subpath_start = *to; 1397 return 0; 1398 } 1399 1400 1401 static FT_Error 1402 ft_stroker_add_reverse_left( FT_Stroker stroker, 1403 FT_Bool open ) 1404 { 1405 FT_StrokeBorder right = stroker->borders + 0; 1406 FT_StrokeBorder left = stroker->borders + 1; 1407 FT_Int new_points; 1408 FT_Error error = 0; 1409 1410 1411 FT_ASSERT( left->start >= 0 ); 1412 1413 new_points = left->num_points - left->start; 1414 if ( new_points > 0 ) 1415 { 1416 error = ft_stroke_border_grow( right, (FT_UInt)new_points ); 1417 if ( error ) 1418 goto Exit; 1419 1420 { 1421 FT_Vector* dst_point = right->points + right->num_points; 1422 FT_Byte* dst_tag = right->tags + right->num_points; 1423 FT_Vector* src_point = left->points + left->num_points - 1; 1424 FT_Byte* src_tag = left->tags + left->num_points - 1; 1425 1426 while ( src_point >= left->points + left->start ) 1427 { 1428 *dst_point = *src_point; 1429 *dst_tag = *src_tag; 1430 1431 if ( open ) 1432 dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END; 1433 else 1434 { 1435 FT_Byte ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END ); 1436 1437 1438 /* switch begin/end tags if necessary */ 1439 if ( ttag == FT_STROKE_TAG_BEGIN || 1440 ttag == FT_STROKE_TAG_END ) 1441 dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END; 1442 1443 } 1444 1445 src_point--; 1446 src_tag--; 1447 dst_point++; 1448 dst_tag++; 1449 } 1450 } 1451 1452 left->num_points = left->start; 1453 right->num_points += new_points; 1454 1455 right->movable = 0; 1456 left->movable = 0; 1457 } 1458 1459 Exit: 1460 return error; 1461 } 1462 1463 1464 /* documentation is in ftstroke.h */ 1465 1466 /* there's a lot of magic in this function! */ 1467 FT_EXPORT_DEF( FT_Error ) 1468 FT_Stroker_EndSubPath( FT_Stroker stroker ) 1469 { 1470 FT_Error error = 0; 1471 1472 if ( stroker->subpath_open ) 1473 { 1474 FT_StrokeBorder right = stroker->borders; 1475 1476 /* All right, this is an opened path, we need to add a cap between */ 1477 /* right & left, add the reverse of left, then add a final cap */ 1478 /* between left & right. */ 1479 error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); 1480 if ( error ) 1481 goto Exit; 1482 1483 /* add reversed points from "left" to "right" */ 1484 error = ft_stroker_add_reverse_left( stroker, 1 ); 1485 if ( error ) 1486 goto Exit; 1487 1488 /* now add the final cap */ 1489 stroker->center = stroker->subpath_start; 1490 error = ft_stroker_cap( stroker, 1491 stroker->subpath_angle + FT_ANGLE_PI, 0 ); 1492 if ( error ) 1493 goto Exit; 1494 1495 /* Now end the right subpath accordingly. The left one is */ 1496 /* rewind and doesn't need further processing. */ 1497 ft_stroke_border_close( right, 0 ); 1498 } 1499 else 1500 { 1501 FT_Angle turn; 1502 FT_Int inside_side; 1503 1504 /* close the path if needed */ 1505 if ( stroker->center.x != stroker->subpath_start.x || 1506 stroker->center.y != stroker->subpath_start.y ) 1507 { 1508 error = FT_Stroker_LineTo( stroker, &stroker->subpath_start ); 1509 if ( error ) 1510 goto Exit; 1511 } 1512 1513 /* process the corner */ 1514 stroker->angle_out = stroker->subpath_angle; 1515 turn = FT_Angle_Diff( stroker->angle_in, 1516 stroker->angle_out ); 1517 1518 /* no specific corner processing is required if the turn is 0 */ 1519 if ( turn != 0 ) 1520 { 1521 /* when we turn to the right, the inside side is 0 */ 1522 inside_side = 0; 1523 1524 /* otherwise, the inside side is 1 */ 1525 if ( turn < 0 ) 1526 inside_side = 1; 1527 1528 error = ft_stroker_inside( stroker, inside_side ); 1529 if ( error ) 1530 goto Exit; 1531 1532 /* process the outside side */ 1533 error = ft_stroker_outside( stroker, 1 - inside_side ); 1534 if ( error ) 1535 goto Exit; 1536 } 1537 1538 /* then end our two subpaths */ 1539 ft_stroke_border_close( stroker->borders + 0, 1 ); 1540 ft_stroke_border_close( stroker->borders + 1, 0 ); 1541 } 1542 1543 Exit: 1544 return error; 1545 } 1546 1547 1548 /* documentation is in ftstroke.h */ 1549 1550 FT_EXPORT_DEF( FT_Error ) 1551 FT_Stroker_GetBorderCounts( FT_Stroker stroker, 1552 FT_StrokerBorder border, 1553 FT_UInt *anum_points, 1554 FT_UInt *anum_contours ) 1555 { 1556 FT_UInt num_points = 0, num_contours = 0; 1557 FT_Error error; 1558 1559 1560 if ( !stroker || border > 1 ) 1561 { 1562 error = FT_Err_Invalid_Argument; 1563 goto Exit; 1564 } 1565 1566 error = ft_stroke_border_get_counts( stroker->borders + border, 1567 &num_points, &num_contours ); 1568 Exit: 1569 if ( anum_points ) 1570 *anum_points = num_points; 1571 1572 if ( anum_contours ) 1573 *anum_contours = num_contours; 1574 1575 return error; 1576 } 1577 1578 1579 /* documentation is in ftstroke.h */ 1580 1581 FT_EXPORT_DEF( FT_Error ) 1582 FT_Stroker_GetCounts( FT_Stroker stroker, 1583 FT_UInt *anum_points, 1584 FT_UInt *anum_contours ) 1585 { 1586 FT_UInt count1, count2, num_points = 0; 1587 FT_UInt count3, count4, num_contours = 0; 1588 FT_Error error; 1589 1590 1591 error = ft_stroke_border_get_counts( stroker->borders + 0, 1592 &count1, &count2 ); 1593 if ( error ) 1594 goto Exit; 1595 1596 error = ft_stroke_border_get_counts( stroker->borders + 1, 1597 &count3, &count4 ); 1598 if ( error ) 1599 goto Exit; 1600 1601 num_points = count1 + count3; 1602 num_contours = count2 + count4; 1603 1604 Exit: 1605 *anum_points = num_points; 1606 *anum_contours = num_contours; 1607 return error; 1608 } 1609 1610 1611 /* documentation is in ftstroke.h */ 1612 1613 FT_EXPORT_DEF( void ) 1614 FT_Stroker_ExportBorder( FT_Stroker stroker, 1615 FT_StrokerBorder border, 1616 FT_Outline* outline ) 1617 { 1618 if ( border == FT_STROKER_BORDER_LEFT || 1619 border == FT_STROKER_BORDER_RIGHT ) 1620 { 1621 FT_StrokeBorder sborder = & stroker->borders[border]; 1622 1623 1624 if ( sborder->valid ) 1625 ft_stroke_border_export( sborder, outline ); 1626 } 1627 } 1628 1629 1630 /* documentation is in ftstroke.h */ 1631 1632 FT_EXPORT_DEF( void ) 1633 FT_Stroker_Export( FT_Stroker stroker, 1634 FT_Outline* outline ) 1635 { 1636 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline ); 1637 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline ); 1638 } 1639 1640 1641 /* documentation is in ftstroke.h */ 1642 1643 /* 1644 * The following is very similar to FT_Outline_Decompose, except 1645 * that we do support opened paths, and do not scale the outline. 1646 */ 1647 FT_EXPORT_DEF( FT_Error ) 1648 FT_Stroker_ParseOutline( FT_Stroker stroker, 1649 FT_Outline* outline, 1650 FT_Bool opened ) 1651 { 1652 FT_Vector v_last; 1653 FT_Vector v_control; 1654 FT_Vector v_start; 1655 1656 FT_Vector* point; 1657 FT_Vector* limit; 1658 char* tags; 1659 1660 FT_Error error; 1661 1662 FT_Int n; /* index of contour in outline */ 1663 FT_UInt first; /* index of first point in contour */ 1664 FT_Int tag; /* current point's state */ 1665 1666 1667 if ( !outline || !stroker ) 1668 return FT_Err_Invalid_Argument; 1669 1670 FT_Stroker_Rewind( stroker ); 1671 1672 first = 0; 1673 1674 for ( n = 0; n < outline->n_contours; n++ ) 1675 { 1676 FT_UInt last; /* index of last point in contour */ 1677 1678 1679 last = outline->contours[n]; 1680 limit = outline->points + last; 1681 1682 /* skip empty points; we don't stroke these */ 1683 if ( last <= first ) 1684 { 1685 first = last + 1; 1686 continue; 1687 } 1688 1689 v_start = outline->points[first]; 1690 v_last = outline->points[last]; 1691 1692 v_control = v_start; 1693 1694 point = outline->points + first; 1695 tags = outline->tags + first; 1696 tag = FT_CURVE_TAG( tags[0] ); 1697 1698 /* A contour cannot start with a cubic control point! */ 1699 if ( tag == FT_CURVE_TAG_CUBIC ) 1700 goto Invalid_Outline; 1701 1702 /* check first point to determine origin */ 1703 if ( tag == FT_CURVE_TAG_CONIC ) 1704 { 1705 /* First point is conic control. Yes, this happens. */ 1706 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 1707 { 1708 /* start at last point if it is on the curve */ 1709 v_start = v_last; 1710 limit--; 1711 } 1712 else 1713 { 1714 /* if both first and last points are conic, */ 1715 /* start at their middle and record its position */ 1716 /* for closure */ 1717 v_start.x = ( v_start.x + v_last.x ) / 2; 1718 v_start.y = ( v_start.y + v_last.y ) / 2; 1719 1720 v_last = v_start; 1721 } 1722 point--; 1723 tags--; 1724 } 1725 1726 error = FT_Stroker_BeginSubPath( stroker, &v_start, opened ); 1727 if ( error ) 1728 goto Exit; 1729 1730 while ( point < limit ) 1731 { 1732 point++; 1733 tags++; 1734 1735 tag = FT_CURVE_TAG( tags[0] ); 1736 switch ( tag ) 1737 { 1738 case FT_CURVE_TAG_ON: /* emit a single line_to */ 1739 { 1740 FT_Vector vec; 1741 1742 1743 vec.x = point->x; 1744 vec.y = point->y; 1745 1746 error = FT_Stroker_LineTo( stroker, &vec ); 1747 if ( error ) 1748 goto Exit; 1749 continue; 1750 } 1751 1752 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 1753 v_control.x = point->x; 1754 v_control.y = point->y; 1755 1756 Do_Conic: 1757 if ( point < limit ) 1758 { 1759 FT_Vector vec; 1760 FT_Vector v_middle; 1761 1762 1763 point++; 1764 tags++; 1765 tag = FT_CURVE_TAG( tags[0] ); 1766 1767 vec = point[0]; 1768 1769 if ( tag == FT_CURVE_TAG_ON ) 1770 { 1771 error = FT_Stroker_ConicTo( stroker, &v_control, &vec ); 1772 if ( error ) 1773 goto Exit; 1774 continue; 1775 } 1776 1777 if ( tag != FT_CURVE_TAG_CONIC ) 1778 goto Invalid_Outline; 1779 1780 v_middle.x = ( v_control.x + vec.x ) / 2; 1781 v_middle.y = ( v_control.y + vec.y ) / 2; 1782 1783 error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); 1784 if ( error ) 1785 goto Exit; 1786 1787 v_control = vec; 1788 goto Do_Conic; 1789 } 1790 1791 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start ); 1792 goto Close; 1793 1794 default: /* FT_CURVE_TAG_CUBIC */ 1795 { 1796 FT_Vector vec1, vec2; 1797 1798 1799 if ( point + 1 > limit || 1800 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 1801 goto Invalid_Outline; 1802 1803 point += 2; 1804 tags += 2; 1805 1806 vec1 = point[-2]; 1807 vec2 = point[-1]; 1808 1809 if ( point <= limit ) 1810 { 1811 FT_Vector vec; 1812 1813 1814 vec = point[0]; 1815 1816 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); 1817 if ( error ) 1818 goto Exit; 1819 continue; 1820 } 1821 1822 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); 1823 goto Close; 1824 } 1825 } 1826 } 1827 1828 Close: 1829 if ( error ) 1830 goto Exit; 1831 1832 error = FT_Stroker_EndSubPath( stroker ); 1833 if ( error ) 1834 goto Exit; 1835 1836 first = last + 1; 1837 } 1838 1839 return 0; 1840 1841 Exit: 1842 return error; 1843 1844 Invalid_Outline: 1845 return FT_Err_Invalid_Outline; 1846 } 1847 1848 1849 extern const FT_Glyph_Class ft_outline_glyph_class; 1850 1851 1852 /* documentation is in ftstroke.h */ 1853 1854 FT_EXPORT_DEF( FT_Error ) 1855 FT_Glyph_Stroke( FT_Glyph *pglyph, 1856 FT_Stroker stroker, 1857 FT_Bool destroy ) 1858 { 1859 FT_Error error = FT_Err_Invalid_Argument; 1860 FT_Glyph glyph = NULL; 1861 1862 1863 if ( pglyph == NULL ) 1864 goto Exit; 1865 1866 glyph = *pglyph; 1867 if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class ) 1868 goto Exit; 1869 1870 { 1871 FT_Glyph copy; 1872 1873 1874 error = FT_Glyph_Copy( glyph, © ); 1875 if ( error ) 1876 goto Exit; 1877 1878 glyph = copy; 1879 } 1880 1881 { 1882 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph; 1883 FT_Outline* outline = &oglyph->outline; 1884 FT_UInt num_points, num_contours; 1885 1886 1887 error = FT_Stroker_ParseOutline( stroker, outline, 0 ); 1888 if ( error ) 1889 goto Fail; 1890 1891 (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours ); 1892 1893 FT_Outline_Done( glyph->library, outline ); 1894 1895 error = FT_Outline_New( glyph->library, 1896 num_points, num_contours, outline ); 1897 if ( error ) 1898 goto Fail; 1899 1900 outline->n_points = 0; 1901 outline->n_contours = 0; 1902 1903 FT_Stroker_Export( stroker, outline ); 1904 } 1905 1906 if ( destroy ) 1907 FT_Done_Glyph( *pglyph ); 1908 1909 *pglyph = glyph; 1910 goto Exit; 1911 1912 Fail: 1913 FT_Done_Glyph( glyph ); 1914 glyph = NULL; 1915 1916 if ( !destroy ) 1917 *pglyph = NULL; 1918 1919 Exit: 1920 return error; 1921 } 1922 1923 1924 /* documentation is in ftstroke.h */ 1925 1926 FT_EXPORT_DEF( FT_Error ) 1927 FT_Glyph_StrokeBorder( FT_Glyph *pglyph, 1928 FT_Stroker stroker, 1929 FT_Bool inside, 1930 FT_Bool destroy ) 1931 { 1932 FT_Error error = FT_Err_Invalid_Argument; 1933 FT_Glyph glyph = NULL; 1934 1935 1936 if ( pglyph == NULL ) 1937 goto Exit; 1938 1939 glyph = *pglyph; 1940 if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class ) 1941 goto Exit; 1942 1943 { 1944 FT_Glyph copy; 1945 1946 1947 error = FT_Glyph_Copy( glyph, © ); 1948 if ( error ) 1949 goto Exit; 1950 1951 glyph = copy; 1952 } 1953 1954 { 1955 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph; 1956 FT_StrokerBorder border; 1957 FT_Outline* outline = &oglyph->outline; 1958 FT_UInt num_points, num_contours; 1959 1960 1961 border = FT_Outline_GetOutsideBorder( outline ); 1962 if ( inside ) 1963 { 1964 if ( border == FT_STROKER_BORDER_LEFT ) 1965 border = FT_STROKER_BORDER_RIGHT; 1966 else 1967 border = FT_STROKER_BORDER_LEFT; 1968 } 1969 1970 error = FT_Stroker_ParseOutline( stroker, outline, 0 ); 1971 if ( error ) 1972 goto Fail; 1973 1974 (void)FT_Stroker_GetBorderCounts( stroker, border, 1975 &num_points, &num_contours ); 1976 1977 FT_Outline_Done( glyph->library, outline ); 1978 1979 error = FT_Outline_New( glyph->library, 1980 num_points, 1981 num_contours, 1982 outline ); 1983 if ( error ) 1984 goto Fail; 1985 1986 outline->n_points = 0; 1987 outline->n_contours = 0; 1988 1989 FT_Stroker_ExportBorder( stroker, border, outline ); 1990 } 1991 1992 if ( destroy ) 1993 FT_Done_Glyph( *pglyph ); 1994 1995 *pglyph = glyph; 1996 goto Exit; 1997 1998 Fail: 1999 FT_Done_Glyph( glyph ); 2000 glyph = NULL; 2001 2002 if ( !destroy ) 2003 *pglyph = NULL; 2004 2005 Exit: 2006 return error; 2007 } 2008 2009 2010/* END */ 2011