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